Roo/util/Observable.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     console.log("BOOSTRAP COMPONENT CONSTRUCTOR");
251     Roo.bootstrap.Component.superclass.constructor.call(this, config);
252        
253     this.addEvents({
254         /**
255          * @event childrenrendered
256          * Fires when the children have been rendered..
257          * @param {Roo.bootstrap.Component} this
258          */
259         "childrenrendered" : true
260         
261         
262         
263     });
264     
265     
266 };
267
268 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
269     
270     
271     allowDomMove : false, // to stop relocations in parent onRender...
272     
273     cls : false,
274     
275     style : false,
276     
277     autoCreate : false,
278     
279     tooltip : null,
280     /**
281      * Initialize Events for the element
282      */
283     initEvents : function() { },
284     
285     xattr : false,
286     
287     parentId : false,
288     
289     can_build_overlaid : true,
290     
291     container_method : false,
292     
293     dataId : false,
294     
295     name : false,
296     
297     parent: function() {
298         // returns the parent component..
299         return Roo.ComponentMgr.get(this.parentId)
300         
301         
302     },
303     
304     // private
305     onRender : function(ct, position)
306     {
307        // Roo.log("Call onRender: " + this.xtype);
308         
309         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
310         
311         if(this.el){
312             if (this.el.attr('xtype')) {
313                 this.el.attr('xtypex', this.el.attr('xtype'));
314                 this.el.dom.removeAttribute('xtype');
315                 
316                 this.initEvents();
317             }
318             
319             return;
320         }
321         
322          
323         
324         var cfg = Roo.apply({},  this.getAutoCreate());
325         
326         cfg.id = this.id || Roo.id();
327         
328         // fill in the extra attributes 
329         if (this.xattr && typeof(this.xattr) =='object') {
330             for (var i in this.xattr) {
331                 cfg[i] = this.xattr[i];
332             }
333         }
334         
335         if(this.dataId){
336             cfg.dataId = this.dataId;
337         }
338         
339         if (this.cls) {
340             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
341         }
342         
343         if (this.style) { // fixme needs to support more complex style data.
344             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
345         }
346         
347         if(this.name){
348             cfg.name = this.name;
349         }
350         
351         this.el = ct.createChild(cfg, position);
352         
353         if (this.tooltip) {
354             this.tooltipEl().attr('tooltip', this.tooltip);
355         }
356         
357         if(this.tabIndex !== undefined){
358             this.el.dom.setAttribute('tabIndex', this.tabIndex);
359         }
360         
361         this.initEvents();
362         
363     },
364     /**
365      * Fetch the element to add children to
366      * @return {Roo.Element} defaults to this.el
367      */
368     getChildContainer : function()
369     {
370         return this.el;
371     },
372     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
373     {
374         return Roo.get(document.body);
375     },
376     
377     /**
378      * Fetch the element to display the tooltip on.
379      * @return {Roo.Element} defaults to this.el
380      */
381     tooltipEl : function()
382     {
383         return this.el;
384     },
385         
386     addxtype  : function(tree,cntr)
387     {
388         var cn = this;
389         
390         cn = Roo.factory(tree);
391         //Roo.log(['addxtype', cn]);
392            
393         cn.parentType = this.xtype; //??
394         cn.parentId = this.id;
395         
396         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
397         if (typeof(cn.container_method) == 'string') {
398             cntr = cn.container_method;
399         }
400         
401         
402         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
403         
404         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
405         
406         var build_from_html =  Roo.XComponent.build_from_html;
407           
408         var is_body  = (tree.xtype == 'Body') ;
409           
410         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
411           
412         var self_cntr_el = Roo.get(this[cntr](false));
413         
414         // do not try and build conditional elements 
415         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
416             return false;
417         }
418         
419         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
420             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
421                 return this.addxtypeChild(tree,cntr, is_body);
422             }
423             
424             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
425                 
426             if(echild){
427                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
428             }
429             
430             Roo.log('skipping render');
431             return cn;
432             
433         }
434         
435         var ret = false;
436         if (!build_from_html) {
437             return false;
438         }
439         
440         // this i think handles overlaying multiple children of the same type
441         // with the sam eelement.. - which might be buggy..
442         while (true) {
443             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
444             
445             if (!echild) {
446                 break;
447             }
448             
449             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
450                 break;
451             }
452             
453             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
454         }
455        
456         return ret;
457     },
458     
459     
460     addxtypeChild : function (tree, cntr, is_body)
461     {
462         Roo.debug && Roo.log('addxtypeChild:' + cntr);
463         var cn = this;
464         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
465         
466         
467         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
468                     (typeof(tree['flexy:foreach']) != 'undefined');
469           
470     
471         
472         skip_children = false;
473         // render the element if it's not BODY.
474         if (!is_body) {
475             
476             // if parent was disabled, then do not try and create the children..
477             if(!this[cntr](true)){
478                 tree.items = [];
479                 return tree;
480             }
481            
482             cn = Roo.factory(tree);
483            
484             cn.parentType = this.xtype; //??
485             cn.parentId = this.id;
486             
487             var build_from_html =  Roo.XComponent.build_from_html;
488             
489             
490             // does the container contain child eleemnts with 'xtype' attributes.
491             // that match this xtype..
492             // note - when we render we create these as well..
493             // so we should check to see if body has xtype set.
494             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
495                
496                 var self_cntr_el = Roo.get(this[cntr](false));
497                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
498                 if (echild) { 
499                     //Roo.log(Roo.XComponent.build_from_html);
500                     //Roo.log("got echild:");
501                     //Roo.log(echild);
502                 }
503                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
504                 // and are not displayed -this causes this to use up the wrong element when matching.
505                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
506                 
507                 
508                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
509                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
510                   
511                   
512                   
513                     cn.el = echild;
514                   //  Roo.log("GOT");
515                     //echild.dom.removeAttribute('xtype');
516                 } else {
517                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
518                     Roo.debug && Roo.log(self_cntr_el);
519                     Roo.debug && Roo.log(echild);
520                     Roo.debug && Roo.log(cn);
521                 }
522             }
523            
524             
525            
526             // if object has flexy:if - then it may or may not be rendered.
527             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
528                 // skip a flexy if element.
529                 Roo.debug && Roo.log('skipping render');
530                 Roo.debug && Roo.log(tree);
531                 if (!cn.el) {
532                     Roo.debug && Roo.log('skipping all children');
533                     skip_children = true;
534                 }
535                 
536              } else {
537                  
538                 // actually if flexy:foreach is found, we really want to create 
539                 // multiple copies here...
540                 //Roo.log('render');
541                 //Roo.log(this[cntr]());
542                 // some elements do not have render methods.. like the layouts...
543                 /*
544                 if(this[cntr](true) === false){
545                     cn.items = [];
546                     return cn;
547                 }
548                 */
549                 cn.render && cn.render(this[cntr](true));
550                 
551              }
552             // then add the element..
553         }
554          
555         // handle the kids..
556         
557         var nitems = [];
558         /*
559         if (typeof (tree.menu) != 'undefined') {
560             tree.menu.parentType = cn.xtype;
561             tree.menu.triggerEl = cn.el;
562             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
563             
564         }
565         */
566         if (!tree.items || !tree.items.length) {
567             cn.items = nitems;
568             //Roo.log(["no children", this]);
569             
570             return cn;
571         }
572          
573         var items = tree.items;
574         delete tree.items;
575         
576         //Roo.log(items.length);
577             // add the items..
578         if (!skip_children) {    
579             for(var i =0;i < items.length;i++) {
580               //  Roo.log(['add child', items[i]]);
581                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
582             }
583         }
584         
585         cn.items = nitems;
586         
587         //Roo.log("fire childrenrendered");
588         
589         cn.fireEvent('childrenrendered', this);
590         
591         return cn;
592     },
593     
594     /**
595      * Set the element that will be used to show or hide
596      */
597     setVisibilityEl : function(el)
598     {
599         this.visibilityEl = el;
600     },
601     
602      /**
603      * Get the element that will be used to show or hide
604      */
605     getVisibilityEl : function()
606     {
607         if (typeof(this.visibilityEl) == 'object') {
608             return this.visibilityEl;
609         }
610         
611         if (typeof(this.visibilityEl) == 'string') {
612             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
613         }
614         
615         return this.getEl();
616     },
617     
618     /**
619      * Show a component - removes 'hidden' class
620      */
621     show : function()
622     {
623         if(!this.getVisibilityEl()){
624             return;
625         }
626          
627         this.getVisibilityEl().removeClass(['hidden','d-none']);
628         
629         this.fireEvent('show', this);
630         
631         
632     },
633     /**
634      * Hide a component - adds 'hidden' class
635      */
636     hide: function()
637     {
638         if(!this.getVisibilityEl()){
639             return;
640         }
641         
642         this.getVisibilityEl().addClass(['hidden','d-none']);
643         
644         this.fireEvent('hide', this);
645         
646     }
647 });
648
649  /*
650  * - LGPL
651  *
652  * element
653  * 
654  */
655
656 /**
657  * @class Roo.bootstrap.Element
658  * @extends Roo.bootstrap.Component
659  * @children Roo.bootstrap.Component
660  * Bootstrap Element class (basically a DIV used to make random stuff )
661  * 
662  * @cfg {String} html contents of the element
663  * @cfg {String} tag tag of the element
664  * @cfg {String} cls class of the element
665  * @cfg {Boolean} preventDefault (true|false) default false
666  * @cfg {Boolean} clickable (true|false) default false
667  * @cfg {String} role default blank - set to button to force cursor pointer
668  
669  * 
670  * @constructor
671  * Create a new Element
672  * @param {Object} config The config object
673  */
674
675 Roo.bootstrap.Element = function(config){
676     Roo.bootstrap.Element.superclass.constructor.call(this, config);
677     
678     this.addEvents({
679         // raw events
680         /**
681          * @event click
682          * When a element is chick
683          * @param {Roo.bootstrap.Element} this
684          * @param {Roo.EventObject} e
685          */
686         "click" : true 
687         
688       
689     });
690 };
691
692 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
693     
694     tag: 'div',
695     cls: '',
696     html: '',
697     preventDefault: false, 
698     clickable: false,
699     tapedTwice : false,
700     role : false,
701     
702     getAutoCreate : function(){
703         
704         var cfg = {
705             tag: this.tag,
706             // cls: this.cls, double assign in parent class Component.js :: onRender
707             html: this.html
708         };
709         if (this.role !== false) {
710             cfg.role = this.role;
711         }
712         
713         return cfg;
714     },
715     
716     initEvents: function() 
717     {
718         Roo.bootstrap.Element.superclass.initEvents.call(this);
719         
720         if(this.clickable){
721             this.el.on('click', this.onClick, this);
722         }
723         
724         
725     },
726     
727     onClick : function(e)
728     {
729         if(this.preventDefault){
730             e.preventDefault();
731         }
732         
733         this.fireEvent('click', this, e); // why was this double click before?
734     },
735     
736     
737     
738
739     
740     
741     getValue : function()
742     {
743         return this.el.dom.innerHTML;
744     },
745     
746     setValue : function(value)
747     {
748         this.el.dom.innerHTML = value;
749     }
750    
751 });
752
753  
754
755  /*
756  * - LGPL
757  *
758  * dropable area
759  * 
760  */
761
762 /**
763  * @class Roo.bootstrap.DropTarget
764  * @extends Roo.bootstrap.Element
765  * Bootstrap DropTarget class
766  
767  * @cfg {string} name dropable name
768  * 
769  * @constructor
770  * Create a new Dropable Area
771  * @param {Object} config The config object
772  */
773
774 Roo.bootstrap.DropTarget = function(config){
775     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
776     
777     this.addEvents({
778         // raw events
779         /**
780          * @event click
781          * When a element is chick
782          * @param {Roo.bootstrap.Element} this
783          * @param {Roo.EventObject} e
784          */
785         "drop" : true
786     });
787 };
788
789 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
790     
791     
792     getAutoCreate : function(){
793         
794          
795     },
796     
797     initEvents: function() 
798     {
799         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
800         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
801             ddGroup: this.name,
802             listeners : {
803                 drop : this.dragDrop.createDelegate(this),
804                 enter : this.dragEnter.createDelegate(this),
805                 out : this.dragOut.createDelegate(this),
806                 over : this.dragOver.createDelegate(this)
807             }
808             
809         });
810         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
811     },
812     
813     dragDrop : function(source,e,data)
814     {
815         // user has to decide how to impliment this.
816         Roo.log('drop');
817         Roo.log(this);
818         //this.fireEvent('drop', this, source, e ,data);
819         return false;
820     },
821     
822     dragEnter : function(n, dd, e, data)
823     {
824         // probably want to resize the element to match the dropped element..
825         Roo.log("enter");
826         this.originalSize = this.el.getSize();
827         this.el.setSize( n.el.getSize());
828         this.dropZone.DDM.refreshCache(this.name);
829         Roo.log([n, dd, e, data]);
830     },
831     
832     dragOut : function(value)
833     {
834         // resize back to normal
835         Roo.log("out");
836         this.el.setSize(this.originalSize);
837         this.dropZone.resetConstraints();
838     },
839     
840     dragOver : function()
841     {
842         // ??? do nothing?
843     }
844    
845 });
846
847  
848
849  /*
850  * - LGPL
851  *
852  * Body
853  *
854  */
855
856 /**
857  * @class Roo.bootstrap.Body
858  * @extends Roo.bootstrap.Component
859  * @children Roo.bootstrap.Component 
860  * @parent none builder
861  * Bootstrap Body class
862  *
863  * @constructor
864  * Create a new body
865  * @param {Object} config The config object
866  */
867
868 Roo.bootstrap.Body = function(config){
869
870     config = config || {};
871
872     Roo.bootstrap.Body.superclass.constructor.call(this, config);
873     this.el = Roo.get(config.el ? config.el : document.body );
874     if (this.cls && this.cls.length) {
875         Roo.get(document.body).addClass(this.cls);
876     }
877 };
878
879 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
880
881     is_body : true,// just to make sure it's constructed?
882
883         autoCreate : {
884         cls: 'container'
885     },
886     onRender : function(ct, position)
887     {
888        /* Roo.log("Roo.bootstrap.Body - onRender");
889         if (this.cls && this.cls.length) {
890             Roo.get(document.body).addClass(this.cls);
891         }
892         // style??? xttr???
893         */
894     }
895
896
897
898
899 });
900 /*
901  * - LGPL
902  *
903  * button group
904  * 
905  */
906
907
908 /**
909  * @class Roo.bootstrap.ButtonGroup
910  * @extends Roo.bootstrap.Component
911  * Bootstrap ButtonGroup class
912  * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
913  * 
914  * @cfg {String} size lg | sm | xs (default empty normal)
915  * @cfg {String} align vertical | justified  (default none)
916  * @cfg {String} direction up | down (default down)
917  * @cfg {Boolean} toolbar false | true
918  * @cfg {Boolean} btn true | false
919  * 
920  * 
921  * @constructor
922  * Create a new Input
923  * @param {Object} config The config object
924  */
925
926 Roo.bootstrap.ButtonGroup = function(config){
927     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
928 };
929
930 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
931     
932     size: '',
933     align: '',
934     direction: '',
935     toolbar: false,
936     btn: true,
937
938     getAutoCreate : function(){
939         var cfg = {
940             cls: 'btn-group',
941             html : null
942         };
943         
944         cfg.html = this.html || cfg.html;
945         
946         if (this.toolbar) {
947             cfg = {
948                 cls: 'btn-toolbar',
949                 html: null
950             };
951             
952             return cfg;
953         }
954         
955         if (['vertical','justified'].indexOf(this.align)!==-1) {
956             cfg.cls = 'btn-group-' + this.align;
957             
958             if (this.align == 'justified') {
959                 console.log(this.items);
960             }
961         }
962         
963         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
964             cfg.cls += ' btn-group-' + this.size;
965         }
966         
967         if (this.direction == 'up') {
968             cfg.cls += ' dropup' ;
969         }
970         
971         return cfg;
972     },
973     /**
974      * Add a button to the group (similar to NavItem API.)
975      */
976     addItem : function(cfg)
977     {
978         var cn = new Roo.bootstrap.Button(cfg);
979         //this.register(cn);
980         cn.parentId = this.id;
981         cn.onRender(this.el, null);
982         return cn;
983     }
984    
985 });
986
987  /*
988  * - LGPL
989  *
990  * button
991  * 
992  */
993
994 /**
995  * @class Roo.bootstrap.Button
996  * @extends Roo.bootstrap.Component
997  * Bootstrap Button class
998  * @cfg {String} html The button content
999  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
1000  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1001  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1002  * @cfg {String} size (lg|sm|xs)
1003  * @cfg {String} tag (a|input|submit)
1004  * @cfg {String} href empty or href
1005  * @cfg {Boolean} disabled default false;
1006  * @cfg {Boolean} isClose default false;
1007  * @cfg {String} glyphicon depricated - use fa
1008  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1009  * @cfg {String} badge text for badge
1010  * @cfg {String} theme (default|glow)  
1011  * @cfg {Boolean} inverse dark themed version
1012  * @cfg {Boolean} toggle is it a slidy toggle button
1013  * @cfg {Boolean} pressed   default null - if the button ahs active state
1014  * @cfg {String} ontext text for on slidy toggle state
1015  * @cfg {String} offtext text for off slidy toggle state
1016  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1017  * @cfg {Boolean} removeClass remove the standard class..
1018  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1019  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1020  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
1021
1022  * @constructor
1023  * Create a new button
1024  * @param {Object} config The config object
1025  */
1026
1027
1028 Roo.bootstrap.Button = function(config){
1029     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1030     
1031     this.addEvents({
1032         // raw events
1033         /**
1034          * @event click
1035          * When a button is pressed
1036          * @param {Roo.bootstrap.Button} btn
1037          * @param {Roo.EventObject} e
1038          */
1039         "click" : true,
1040         /**
1041          * @event dblclick
1042          * When a button is double clicked
1043          * @param {Roo.bootstrap.Button} btn
1044          * @param {Roo.EventObject} e
1045          */
1046         "dblclick" : true,
1047          /**
1048          * @event toggle
1049          * After the button has been toggles
1050          * @param {Roo.bootstrap.Button} btn
1051          * @param {Roo.EventObject} e
1052          * @param {boolean} pressed (also available as button.pressed)
1053          */
1054         "toggle" : true
1055     });
1056 };
1057
1058 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1059     html: false,
1060     active: false,
1061     weight: '',
1062     badge_weight: '',
1063     outline : false,
1064     size: '',
1065     tag: 'button',
1066     href: '',
1067     disabled: false,
1068     isClose: false,
1069     glyphicon: '',
1070     fa: '',
1071     badge: '',
1072     theme: 'default',
1073     inverse: false,
1074     
1075     toggle: false,
1076     ontext: 'ON',
1077     offtext: 'OFF',
1078     defaulton: true,
1079     preventDefault: true,
1080     removeClass: false,
1081     name: false,
1082     target: false,
1083     group : false,
1084      
1085     pressed : null,
1086      
1087     
1088     getAutoCreate : function(){
1089         
1090         var cfg = {
1091             tag : 'button',
1092             cls : 'roo-button',
1093             html: ''
1094         };
1095         
1096         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1097             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1098             this.tag = 'button';
1099         } else {
1100             cfg.tag = this.tag;
1101         }
1102         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1103         
1104         if (this.toggle == true) {
1105             cfg={
1106                 tag: 'div',
1107                 cls: 'slider-frame roo-button',
1108                 cn: [
1109                     {
1110                         tag: 'span',
1111                         'data-on-text':'ON',
1112                         'data-off-text':'OFF',
1113                         cls: 'slider-button',
1114                         html: this.offtext
1115                     }
1116                 ]
1117             };
1118             // why are we validating the weights?
1119             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1120                 cfg.cls +=  ' ' + this.weight;
1121             }
1122             
1123             return cfg;
1124         }
1125         
1126         if (this.isClose) {
1127             cfg.cls += ' close';
1128             
1129             cfg["aria-hidden"] = true;
1130             
1131             cfg.html = "&times;";
1132             
1133             return cfg;
1134         }
1135              
1136         
1137         if (this.theme==='default') {
1138             cfg.cls = 'btn roo-button';
1139             
1140             //if (this.parentType != 'Navbar') {
1141             this.weight = this.weight.length ?  this.weight : 'default';
1142             //}
1143             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1144                 
1145                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1146                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1147                 cfg.cls += ' btn-' + outline + weight;
1148                 if (this.weight == 'default') {
1149                     // BC
1150                     cfg.cls += ' btn-' + this.weight;
1151                 }
1152             }
1153         } else if (this.theme==='glow') {
1154             
1155             cfg.tag = 'a';
1156             cfg.cls = 'btn-glow roo-button';
1157             
1158             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1159                 
1160                 cfg.cls += ' ' + this.weight;
1161             }
1162         }
1163    
1164         
1165         if (this.inverse) {
1166             this.cls += ' inverse';
1167         }
1168         
1169         
1170         if (this.active || this.pressed === true) {
1171             cfg.cls += ' active';
1172         }
1173         
1174         if (this.disabled) {
1175             cfg.disabled = 'disabled';
1176         }
1177         
1178         if (this.items) {
1179             Roo.log('changing to ul' );
1180             cfg.tag = 'ul';
1181             this.glyphicon = 'caret';
1182             if (Roo.bootstrap.version == 4) {
1183                 this.fa = 'caret-down';
1184             }
1185             
1186         }
1187         
1188         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1189          
1190         //gsRoo.log(this.parentType);
1191         if (this.parentType === 'Navbar' && !this.parent().bar) {
1192             Roo.log('changing to li?');
1193             
1194             cfg.tag = 'li';
1195             
1196             cfg.cls = '';
1197             cfg.cn =  [{
1198                 tag : 'a',
1199                 cls : 'roo-button',
1200                 html : this.html,
1201                 href : this.href || '#'
1202             }];
1203             if (this.menu) {
1204                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1205                 cfg.cls += ' dropdown';
1206             }   
1207             
1208             delete cfg.html;
1209             
1210         }
1211         
1212        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1213         
1214         if (this.glyphicon) {
1215             cfg.html = ' ' + cfg.html;
1216             
1217             cfg.cn = [
1218                 {
1219                     tag: 'span',
1220                     cls: 'glyphicon glyphicon-' + this.glyphicon
1221                 }
1222             ];
1223         }
1224         if (this.fa) {
1225             cfg.html = ' ' + cfg.html;
1226             
1227             cfg.cn = [
1228                 {
1229                     tag: 'i',
1230                     cls: 'fa fas fa-' + this.fa
1231                 }
1232             ];
1233         }
1234         
1235         if (this.badge) {
1236             cfg.html += ' ';
1237             
1238             cfg.tag = 'a';
1239             
1240 //            cfg.cls='btn roo-button';
1241             
1242             cfg.href=this.href;
1243             
1244             var value = cfg.html;
1245             
1246             if(this.glyphicon){
1247                 value = {
1248                     tag: 'span',
1249                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1250                     html: this.html
1251                 };
1252             }
1253             if(this.fa){
1254                 value = {
1255                     tag: 'i',
1256                     cls: 'fa fas fa-' + this.fa,
1257                     html: this.html
1258                 };
1259             }
1260             
1261             var bw = this.badge_weight.length ? this.badge_weight :
1262                 (this.weight.length ? this.weight : 'secondary');
1263             bw = bw == 'default' ? 'secondary' : bw;
1264             
1265             cfg.cn = [
1266                 value,
1267                 {
1268                     tag: 'span',
1269                     cls: 'badge badge-' + bw,
1270                     html: this.badge
1271                 }
1272             ];
1273             
1274             cfg.html='';
1275         }
1276         
1277         if (this.menu) {
1278             cfg.cls += ' dropdown';
1279             cfg.html = typeof(cfg.html) != 'undefined' ?
1280                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1281         }
1282         
1283         if (cfg.tag !== 'a' && this.href !== '') {
1284             throw "Tag must be a to set href.";
1285         } else if (this.href.length > 0) {
1286             cfg.href = this.href;
1287         }
1288         
1289         if(this.removeClass){
1290             cfg.cls = '';
1291         }
1292         
1293         if(this.target){
1294             cfg.target = this.target;
1295         }
1296         
1297         return cfg;
1298     },
1299     initEvents: function() {
1300        // Roo.log('init events?');
1301 //        Roo.log(this.el.dom);
1302         // add the menu...
1303         
1304         if (typeof (this.menu) != 'undefined') {
1305             this.menu.parentType = this.xtype;
1306             this.menu.triggerEl = this.el;
1307             this.addxtype(Roo.apply({}, this.menu));
1308         }
1309
1310
1311         if (this.el.hasClass('roo-button')) {
1312              this.el.on('click', this.onClick, this);
1313              this.el.on('dblclick', this.onDblClick, this);
1314         } else {
1315              this.el.select('.roo-button').on('click', this.onClick, this);
1316              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1317              
1318         }
1319         // why?
1320         if(this.removeClass){
1321             this.el.on('click', this.onClick, this);
1322         }
1323         
1324         if (this.group === true) {
1325              if (this.pressed === false || this.pressed === true) {
1326                 // nothing
1327             } else {
1328                 this.pressed = false;
1329                 this.setActive(this.pressed);
1330             }
1331             
1332         }
1333         
1334         this.el.enableDisplayMode();
1335         
1336     },
1337     onClick : function(e)
1338     {
1339         if (this.disabled) {
1340             return;
1341         }
1342         
1343         Roo.log('button on click ');
1344         if(this.href === '' || this.preventDefault){
1345             e.preventDefault();
1346         }
1347         
1348         if (this.group) {
1349             if (this.pressed) {
1350                 // do nothing -
1351                 return;
1352             }
1353             this.setActive(true);
1354             var pi = this.parent().items;
1355             for (var i = 0;i < pi.length;i++) {
1356                 if (this == pi[i]) {
1357                     continue;
1358                 }
1359                 if (pi[i].el.hasClass('roo-button')) {
1360                     pi[i].setActive(false);
1361                 }
1362             }
1363             this.fireEvent('click', this, e);            
1364             return;
1365         }
1366         
1367         if (this.pressed === true || this.pressed === false) {
1368             this.toggleActive(e);
1369         }
1370         
1371         
1372         this.fireEvent('click', this, e);
1373     },
1374     onDblClick: function(e)
1375     {
1376         if (this.disabled) {
1377             return;
1378         }
1379         if(this.preventDefault){
1380             e.preventDefault();
1381         }
1382         this.fireEvent('dblclick', this, e);
1383     },
1384     /**
1385      * Enables this button
1386      */
1387     enable : function()
1388     {
1389         this.disabled = false;
1390         this.el.removeClass('disabled');
1391         this.el.dom.removeAttribute("disabled");
1392     },
1393     
1394     /**
1395      * Disable this button
1396      */
1397     disable : function()
1398     {
1399         this.disabled = true;
1400         this.el.addClass('disabled');
1401         this.el.attr("disabled", "disabled")
1402     },
1403      /**
1404      * sets the active state on/off, 
1405      * @param {Boolean} state (optional) Force a particular state
1406      */
1407     setActive : function(v) {
1408         
1409         this.el[v ? 'addClass' : 'removeClass']('active');
1410         this.pressed = v;
1411     },
1412      /**
1413      * toggles the current active state 
1414      */
1415     toggleActive : function(e)
1416     {
1417         this.setActive(!this.pressed); // this modifies pressed...
1418         this.fireEvent('toggle', this, e, this.pressed);
1419     },
1420      /**
1421      * get the current active state
1422      * @return {boolean} true if it's active
1423      */
1424     isActive : function()
1425     {
1426         return this.el.hasClass('active');
1427     },
1428     /**
1429      * set the text of the first selected button
1430      */
1431     setText : function(str)
1432     {
1433         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1434     },
1435     /**
1436      * get the text of the first selected button
1437      */
1438     getText : function()
1439     {
1440         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1441     },
1442     
1443     setWeight : function(str)
1444     {
1445         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1446         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1447         this.weight = str;
1448         var outline = this.outline ? 'outline-' : '';
1449         if (str == 'default') {
1450             this.el.addClass('btn-default btn-outline-secondary');        
1451             return;
1452         }
1453         this.el.addClass('btn-' + outline + str);        
1454     }
1455     
1456     
1457 });
1458 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1459
1460 Roo.bootstrap.Button.weights = [
1461     'default',
1462     'secondary' ,
1463     'primary',
1464     'success',
1465     'info',
1466     'warning',
1467     'danger',
1468     'link',
1469     'light',
1470     'dark'              
1471    
1472 ];/*
1473  * - LGPL
1474  *
1475  * column
1476  * 
1477  */
1478
1479 /**
1480  * @class Roo.bootstrap.Column
1481  * @extends Roo.bootstrap.Component
1482  * @children Roo.bootstrap.Component
1483  * Bootstrap Column class
1484  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1485  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1486  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1487  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1488  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1489  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1490  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1491  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1492  *
1493  * 
1494  * @cfg {Boolean} hidden (true|false) hide the element
1495  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1496  * @cfg {String} fa (ban|check|...) font awesome icon
1497  * @cfg {Number} fasize (1|2|....) font awsome size
1498
1499  * @cfg {String} icon (info-sign|check|...) glyphicon name
1500
1501  * @cfg {String} html content of column.
1502  * 
1503  * @constructor
1504  * Create a new Column
1505  * @param {Object} config The config object
1506  */
1507
1508 Roo.bootstrap.Column = function(config){
1509     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1510 };
1511
1512 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1513     
1514     xs: false,
1515     sm: false,
1516     md: false,
1517     lg: false,
1518     xsoff: false,
1519     smoff: false,
1520     mdoff: false,
1521     lgoff: false,
1522     html: '',
1523     offset: 0,
1524     alert: false,
1525     fa: false,
1526     icon : false,
1527     hidden : false,
1528     fasize : 1,
1529     
1530     getAutoCreate : function(){
1531         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1532         
1533         cfg = {
1534             tag: 'div',
1535             cls: 'column'
1536         };
1537         
1538         var settings=this;
1539         var sizes =   ['xs','sm','md','lg'];
1540         sizes.map(function(size ,ix){
1541             //Roo.log( size + ':' + settings[size]);
1542             
1543             if (settings[size+'off'] !== false) {
1544                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1545             }
1546             
1547             if (settings[size] === false) {
1548                 return;
1549             }
1550             
1551             if (!settings[size]) { // 0 = hidden
1552                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1553                 // bootsrap4
1554                 for (var i = ix; i > -1; i--) {
1555                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1556                 }
1557                 
1558                 
1559                 return;
1560             }
1561             cfg.cls += ' col-' + size + '-' + settings[size] + (
1562                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1563             );
1564             
1565         });
1566         
1567         if (this.hidden) {
1568             cfg.cls += ' hidden';
1569         }
1570         
1571         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1572             cfg.cls +=' alert alert-' + this.alert;
1573         }
1574         
1575         
1576         if (this.html.length) {
1577             cfg.html = this.html;
1578         }
1579         if (this.fa) {
1580             var fasize = '';
1581             if (this.fasize > 1) {
1582                 fasize = ' fa-' + this.fasize + 'x';
1583             }
1584             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1585             
1586             
1587         }
1588         if (this.icon) {
1589             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1590         }
1591         
1592         return cfg;
1593     }
1594    
1595 });
1596
1597  
1598
1599  /*
1600  * - LGPL
1601  *
1602  * page container.
1603  * 
1604  */
1605
1606
1607 /**
1608  * @class Roo.bootstrap.Container
1609  * @extends Roo.bootstrap.Component
1610  * @children Roo.bootstrap.Component
1611  * @parent builder
1612  * Bootstrap Container class
1613  * @cfg {Boolean} jumbotron is it a jumbotron element
1614  * @cfg {String} html content of element
1615  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1616  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1617  * @cfg {String} header content of header (for panel)
1618  * @cfg {String} footer content of footer (for panel)
1619  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1620  * @cfg {String} tag (header|aside|section) type of HTML tag.
1621  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1622  * @cfg {String} fa font awesome icon
1623  * @cfg {String} icon (info-sign|check|...) glyphicon name
1624  * @cfg {Boolean} hidden (true|false) hide the element
1625  * @cfg {Boolean} expandable (true|false) default false
1626  * @cfg {Boolean} expanded (true|false) default true
1627  * @cfg {String} rheader contet on the right of header
1628  * @cfg {Boolean} clickable (true|false) default false
1629
1630  *     
1631  * @constructor
1632  * Create a new Container
1633  * @param {Object} config The config object
1634  */
1635
1636 Roo.bootstrap.Container = function(config){
1637     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1638     
1639     this.addEvents({
1640         // raw events
1641          /**
1642          * @event expand
1643          * After the panel has been expand
1644          * 
1645          * @param {Roo.bootstrap.Container} this
1646          */
1647         "expand" : true,
1648         /**
1649          * @event collapse
1650          * After the panel has been collapsed
1651          * 
1652          * @param {Roo.bootstrap.Container} this
1653          */
1654         "collapse" : true,
1655         /**
1656          * @event click
1657          * When a element is chick
1658          * @param {Roo.bootstrap.Container} this
1659          * @param {Roo.EventObject} e
1660          */
1661         "click" : true
1662     });
1663 };
1664
1665 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1666     
1667     jumbotron : false,
1668     well: '',
1669     panel : '',
1670     header: '',
1671     footer : '',
1672     sticky: '',
1673     tag : false,
1674     alert : false,
1675     fa: false,
1676     icon : false,
1677     expandable : false,
1678     rheader : '',
1679     expanded : true,
1680     clickable: false,
1681   
1682      
1683     getChildContainer : function() {
1684         
1685         if(!this.el){
1686             return false;
1687         }
1688         
1689         if (this.panel.length) {
1690             return this.el.select('.panel-body',true).first();
1691         }
1692         
1693         return this.el;
1694     },
1695     
1696     
1697     getAutoCreate : function(){
1698         
1699         var cfg = {
1700             tag : this.tag || 'div',
1701             html : '',
1702             cls : ''
1703         };
1704         if (this.jumbotron) {
1705             cfg.cls = 'jumbotron';
1706         }
1707         
1708         
1709         
1710         // - this is applied by the parent..
1711         //if (this.cls) {
1712         //    cfg.cls = this.cls + '';
1713         //}
1714         
1715         if (this.sticky.length) {
1716             
1717             var bd = Roo.get(document.body);
1718             if (!bd.hasClass('bootstrap-sticky')) {
1719                 bd.addClass('bootstrap-sticky');
1720                 Roo.select('html',true).setStyle('height', '100%');
1721             }
1722              
1723             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1724         }
1725         
1726         
1727         if (this.well.length) {
1728             switch (this.well) {
1729                 case 'lg':
1730                 case 'sm':
1731                     cfg.cls +=' well well-' +this.well;
1732                     break;
1733                 default:
1734                     cfg.cls +=' well';
1735                     break;
1736             }
1737         }
1738         
1739         if (this.hidden) {
1740             cfg.cls += ' hidden';
1741         }
1742         
1743         
1744         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1745             cfg.cls +=' alert alert-' + this.alert;
1746         }
1747         
1748         var body = cfg;
1749         
1750         if (this.panel.length) {
1751             cfg.cls += ' panel panel-' + this.panel;
1752             cfg.cn = [];
1753             if (this.header.length) {
1754                 
1755                 var h = [];
1756                 
1757                 if(this.expandable){
1758                     
1759                     cfg.cls = cfg.cls + ' expandable';
1760                     
1761                     h.push({
1762                         tag: 'i',
1763                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1764                     });
1765                     
1766                 }
1767                 
1768                 h.push(
1769                     {
1770                         tag: 'span',
1771                         cls : 'panel-title',
1772                         html : (this.expandable ? '&nbsp;' : '') + this.header
1773                     },
1774                     {
1775                         tag: 'span',
1776                         cls: 'panel-header-right',
1777                         html: this.rheader
1778                     }
1779                 );
1780                 
1781                 cfg.cn.push({
1782                     cls : 'panel-heading',
1783                     style : this.expandable ? 'cursor: pointer' : '',
1784                     cn : h
1785                 });
1786                 
1787             }
1788             
1789             body = false;
1790             cfg.cn.push({
1791                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1792                 html : this.html
1793             });
1794             
1795             
1796             if (this.footer.length) {
1797                 cfg.cn.push({
1798                     cls : 'panel-footer',
1799                     html : this.footer
1800                     
1801                 });
1802             }
1803             
1804         }
1805         
1806         if (body) {
1807             body.html = this.html || cfg.html;
1808             // prefix with the icons..
1809             if (this.fa) {
1810                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1811             }
1812             if (this.icon) {
1813                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1814             }
1815             
1816             
1817         }
1818         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1819             cfg.cls =  'container';
1820         }
1821         
1822         return cfg;
1823     },
1824     
1825     initEvents: function() 
1826     {
1827         if(this.expandable){
1828             var headerEl = this.headerEl();
1829         
1830             if(headerEl){
1831                 headerEl.on('click', this.onToggleClick, this);
1832             }
1833         }
1834         
1835         if(this.clickable){
1836             this.el.on('click', this.onClick, this);
1837         }
1838         
1839     },
1840     
1841     onToggleClick : function()
1842     {
1843         var headerEl = this.headerEl();
1844         
1845         if(!headerEl){
1846             return;
1847         }
1848         
1849         if(this.expanded){
1850             this.collapse();
1851             return;
1852         }
1853         
1854         this.expand();
1855     },
1856     
1857     expand : function()
1858     {
1859         if(this.fireEvent('expand', this)) {
1860             
1861             this.expanded = true;
1862             
1863             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1864             
1865             this.el.select('.panel-body',true).first().removeClass('hide');
1866             
1867             var toggleEl = this.toggleEl();
1868
1869             if(!toggleEl){
1870                 return;
1871             }
1872
1873             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1874         }
1875         
1876     },
1877     
1878     collapse : function()
1879     {
1880         if(this.fireEvent('collapse', this)) {
1881             
1882             this.expanded = false;
1883             
1884             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1885             this.el.select('.panel-body',true).first().addClass('hide');
1886         
1887             var toggleEl = this.toggleEl();
1888
1889             if(!toggleEl){
1890                 return;
1891             }
1892
1893             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1894         }
1895     },
1896     
1897     toggleEl : function()
1898     {
1899         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1900             return;
1901         }
1902         
1903         return this.el.select('.panel-heading .fa',true).first();
1904     },
1905     
1906     headerEl : function()
1907     {
1908         if(!this.el || !this.panel.length || !this.header.length){
1909             return;
1910         }
1911         
1912         return this.el.select('.panel-heading',true).first()
1913     },
1914     
1915     bodyEl : function()
1916     {
1917         if(!this.el || !this.panel.length){
1918             return;
1919         }
1920         
1921         return this.el.select('.panel-body',true).first()
1922     },
1923     
1924     titleEl : function()
1925     {
1926         if(!this.el || !this.panel.length || !this.header.length){
1927             return;
1928         }
1929         
1930         return this.el.select('.panel-title',true).first();
1931     },
1932     
1933     setTitle : function(v)
1934     {
1935         var titleEl = this.titleEl();
1936         
1937         if(!titleEl){
1938             return;
1939         }
1940         
1941         titleEl.dom.innerHTML = v;
1942     },
1943     
1944     getTitle : function()
1945     {
1946         
1947         var titleEl = this.titleEl();
1948         
1949         if(!titleEl){
1950             return '';
1951         }
1952         
1953         return titleEl.dom.innerHTML;
1954     },
1955     
1956     setRightTitle : function(v)
1957     {
1958         var t = this.el.select('.panel-header-right',true).first();
1959         
1960         if(!t){
1961             return;
1962         }
1963         
1964         t.dom.innerHTML = v;
1965     },
1966     
1967     onClick : function(e)
1968     {
1969         e.preventDefault();
1970         
1971         this.fireEvent('click', this, e);
1972     }
1973 });
1974
1975  /**
1976  * @class Roo.bootstrap.Card
1977  * @extends Roo.bootstrap.Component
1978  * @children Roo.bootstrap.Component
1979  * @licence LGPL
1980  * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1981  *
1982  *
1983  * possible... may not be implemented..
1984  * @cfg {String} header_image  src url of image.
1985  * @cfg {String|Object} header
1986  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1987  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1988  * 
1989  * @cfg {String} title
1990  * @cfg {String} subtitle
1991  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1992  * @cfg {String} footer
1993  
1994  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1995  * 
1996  * @cfg {String} margin (0|1|2|3|4|5|auto)
1997  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1998  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1999  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
2000  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2001  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2002  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2003  *
2004  * @cfg {String} padding (0|1|2|3|4|5)
2005  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2006  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2007  * @cfg {String} padding_left (0|1|2|3|4|5)
2008  * @cfg {String} padding_right (0|1|2|3|4|5)
2009  * @cfg {String} padding_x (0|1|2|3|4|5)
2010  * @cfg {String} padding_y (0|1|2|3|4|5)
2011  *
2012  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2016  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2017  
2018  * @config {Boolean} dragable  if this card can be dragged.
2019  * @config {String} drag_group  group for drag
2020  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2021  * @config {String} drop_group  group for drag
2022  * 
2023  * @config {Boolean} collapsable can the body be collapsed.
2024  * @config {Boolean} collapsed is the body collapsed when rendered...
2025  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2026  * @config {Boolean} rotated is the body rotated when rendered...
2027  * 
2028  * @constructor
2029  * Create a new Container
2030  * @param {Object} config The config object
2031  */
2032
2033 Roo.bootstrap.Card = function(config){
2034     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2035     
2036     this.addEvents({
2037          // raw events
2038         /**
2039          * @event drop
2040          * When a element a card is dropped
2041          * @param {Roo.bootstrap.Card} this
2042          *
2043          * 
2044          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2045          * @param {String} position 'above' or 'below'
2046          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2047         
2048          */
2049         'drop' : true,
2050          /**
2051          * @event rotate
2052          * When a element a card is rotate
2053          * @param {Roo.bootstrap.Card} this
2054          * @param {Roo.Element} n the node being dropped?
2055          * @param {Boolean} rotate status
2056          */
2057         'rotate' : true,
2058         /**
2059          * @event cardover
2060          * When a card element is dragged over ready to drop (return false to block dropable)
2061          * @param {Roo.bootstrap.Card} this
2062          * @param {Object} data from dragdrop 
2063          */
2064          'cardover' : true
2065          
2066     });
2067 };
2068
2069
2070 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2071     
2072     
2073     weight : '',
2074     
2075     margin: '', /// may be better in component?
2076     margin_top: '', 
2077     margin_bottom: '', 
2078     margin_left: '',
2079     margin_right: '',
2080     margin_x: '',
2081     margin_y: '',
2082     
2083     padding : '',
2084     padding_top: '', 
2085     padding_bottom: '', 
2086     padding_left: '',
2087     padding_right: '',
2088     padding_x: '',
2089     padding_y: '',
2090     
2091     display: '', 
2092     display_xs: '', 
2093     display_sm: '', 
2094     display_lg: '',
2095     display_xl: '',
2096  
2097     header_image  : '',
2098     header : '',
2099     header_size : 0,
2100     title : '',
2101     subtitle : '',
2102     html : '',
2103     footer: '',
2104
2105     collapsable : false,
2106     collapsed : false,
2107     rotateable : false,
2108     rotated : false,
2109     
2110     dragable : false,
2111     drag_group : false,
2112     dropable : false,
2113     drop_group : false,
2114     childContainer : false,
2115     dropEl : false, /// the dom placeholde element that indicates drop location.
2116     containerEl: false, // body container
2117     bodyEl: false, // card-body
2118     headerContainerEl : false, //
2119     headerEl : false,
2120     header_imageEl : false,
2121     
2122     
2123     layoutCls : function()
2124     {
2125         var cls = '';
2126         var t = this;
2127         Roo.log(this.margin_bottom.length);
2128         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2129             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2130             
2131             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2132                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2133             }
2134             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2135                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2136             }
2137         });
2138         
2139         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2140             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2141                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2142             }
2143         });
2144         
2145         // more generic support?
2146         if (this.hidden) {
2147             cls += ' d-none';
2148         }
2149         
2150         return cls;
2151     },
2152  
2153        // Roo.log("Call onRender: " + this.xtype);
2154         /*  We are looking at something like this.
2155 <div class="card">
2156     <img src="..." class="card-img-top" alt="...">
2157     <div class="card-body">
2158         <h5 class="card-title">Card title</h5>
2159          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2160
2161         >> this bit is really the body...
2162         <div> << we will ad dthis in hopefully it will not break shit.
2163         
2164         ** card text does not actually have any styling...
2165         
2166             <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>
2167         
2168         </div> <<
2169           <a href="#" class="card-link">Card link</a>
2170           
2171     </div>
2172     <div class="card-footer">
2173         <small class="text-muted">Last updated 3 mins ago</small>
2174     </div>
2175 </div>
2176          */
2177     getAutoCreate : function(){
2178         
2179         var cfg = {
2180             tag : 'div',
2181             cls : 'card',
2182             cn : [ ]
2183         };
2184         
2185         if (this.weight.length && this.weight != 'light') {
2186             cfg.cls += ' text-white';
2187         } else {
2188             cfg.cls += ' text-dark'; // need as it's nested..
2189         }
2190         if (this.weight.length) {
2191             cfg.cls += ' bg-' + this.weight;
2192         }
2193         
2194         cfg.cls += ' ' + this.layoutCls(); 
2195         
2196         var hdr = false;
2197         var hdr_ctr = false;
2198         if (this.header.length) {
2199             hdr = {
2200                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2201                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2202                 cn : []
2203             };
2204             cfg.cn.push(hdr);
2205             hdr_ctr = hdr;
2206         } else {
2207             hdr = {
2208                 tag : 'div',
2209                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2210                 cn : []
2211             };
2212             cfg.cn.push(hdr);
2213             hdr_ctr = hdr;
2214         }
2215         if (this.collapsable) {
2216             hdr_ctr = {
2217             tag : 'a',
2218             cls : 'd-block user-select-none',
2219             cn: [
2220                     {
2221                         tag: 'i',
2222                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2223                     }
2224                    
2225                 ]
2226             };
2227             hdr.cn.push(hdr_ctr);
2228         }
2229         
2230         hdr_ctr.cn.push(        {
2231             tag: 'span',
2232             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2233             html : this.header
2234         });
2235         
2236         
2237         if (this.header_image.length) {
2238             cfg.cn.push({
2239                 tag : 'img',
2240                 cls : 'card-img-top',
2241                 src: this.header_image // escape?
2242             });
2243         } else {
2244             cfg.cn.push({
2245                     tag : 'div',
2246                     cls : 'card-img-top d-none' 
2247                 });
2248         }
2249             
2250         var body = {
2251             tag : 'div',
2252             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2253             cn : []
2254         };
2255         var obody = body;
2256         if (this.collapsable || this.rotateable) {
2257             obody = {
2258                 tag: 'div',
2259                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2260                 cn : [  body ]
2261             };
2262         }
2263         
2264         cfg.cn.push(obody);
2265         
2266         if (this.title.length) {
2267             body.cn.push({
2268                 tag : 'div',
2269                 cls : 'card-title',
2270                 src: this.title // escape?
2271             });
2272         }  
2273         
2274         if (this.subtitle.length) {
2275             body.cn.push({
2276                 tag : 'div',
2277                 cls : 'card-title',
2278                 src: this.subtitle // escape?
2279             });
2280         }
2281         
2282         body.cn.push({
2283             tag : 'div',
2284             cls : 'roo-card-body-ctr'
2285         });
2286         
2287         if (this.html.length) {
2288             body.cn.push({
2289                 tag: 'div',
2290                 html : this.html
2291             });
2292         }
2293         // fixme ? handle objects?
2294         
2295         if (this.footer.length) {
2296            
2297             cfg.cn.push({
2298                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2299                 html : this.footer
2300             });
2301             
2302         } else {
2303             cfg.cn.push({cls : 'card-footer d-none'});
2304         }
2305         
2306         // footer...
2307         
2308         return cfg;
2309     },
2310     
2311     
2312     getCardHeader : function()
2313     {
2314         var  ret = this.el.select('.card-header',true).first();
2315         if (ret.hasClass('d-none')) {
2316             ret.removeClass('d-none');
2317         }
2318         
2319         return ret;
2320     },
2321     getCardFooter : function()
2322     {
2323         var  ret = this.el.select('.card-footer',true).first();
2324         if (ret.hasClass('d-none')) {
2325             ret.removeClass('d-none');
2326         }
2327         
2328         return ret;
2329     },
2330     getCardImageTop : function()
2331     {
2332         var  ret = this.header_imageEl;
2333         if (ret.hasClass('d-none')) {
2334             ret.removeClass('d-none');
2335         }
2336             
2337         return ret;
2338     },
2339     
2340     getChildContainer : function()
2341     {
2342         
2343         if(!this.el){
2344             return false;
2345         }
2346         return this.el.select('.roo-card-body-ctr',true).first();    
2347     },
2348     
2349     initEvents: function() 
2350     {
2351         this.bodyEl = this.el.select('.card-body',true).first(); 
2352         this.containerEl = this.getChildContainer();
2353         if(this.dragable){
2354             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2355                     containerScroll: true,
2356                     ddGroup: this.drag_group || 'default_card_drag_group'
2357             });
2358             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2359         }
2360         if (this.dropable) {
2361             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2362                 containerScroll: true,
2363                 ddGroup: this.drop_group || 'default_card_drag_group'
2364             });
2365             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2366             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2367             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2368             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2369             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2370         }
2371         
2372         if (this.collapsable) {
2373             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2374         }
2375         if (this.rotateable) {
2376             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2377         }
2378         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2379          
2380         this.footerEl = this.el.select('.card-footer',true).first();
2381         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2382         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2383         this.headerEl = this.el.select('.card-header',true).first();
2384         
2385         if (this.rotated) {
2386             this.el.addClass('roo-card-rotated');
2387             this.fireEvent('rotate', this, true);
2388         }
2389         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2390         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2391         
2392     },
2393     getDragData : function(e)
2394     {
2395         var target = this.getEl();
2396         if (target) {
2397             //this.handleSelection(e);
2398             
2399             var dragData = {
2400                 source: this,
2401                 copy: false,
2402                 nodes: this.getEl(),
2403                 records: []
2404             };
2405             
2406             
2407             dragData.ddel = target.dom ;    // the div element
2408             Roo.log(target.getWidth( ));
2409             dragData.ddel.style.width = target.getWidth() + 'px';
2410             
2411             return dragData;
2412         }
2413         return false;
2414     },
2415     /**
2416     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2417     *    whole Element becomes the target, and this causes the drop gesture to append.
2418     *
2419     *    Returns an object:
2420     *     {
2421            
2422            position : 'below' or 'above'
2423            card  : relateive to card OBJECT (or true for no cards listed)
2424            items_n : relative to nth item in list
2425            card_n : relative to  nth card in list
2426     }
2427     *
2428     *    
2429     */
2430     getTargetFromEvent : function(e, dragged_card_el)
2431     {
2432         var target = e.getTarget();
2433         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2434             target = target.parentNode;
2435         }
2436         
2437         var ret = {
2438             position: '',
2439             cards : [],
2440             card_n : -1,
2441             items_n : -1,
2442             card : false 
2443         };
2444         
2445         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2446         // see if target is one of the 'cards'...
2447         
2448         
2449         //Roo.log(this.items.length);
2450         var pos = false;
2451         
2452         var last_card_n = 0;
2453         var cards_len  = 0;
2454         for (var i = 0;i< this.items.length;i++) {
2455             
2456             if (!this.items[i].el.hasClass('card')) {
2457                  continue;
2458             }
2459             pos = this.getDropPoint(e, this.items[i].el.dom);
2460             
2461             cards_len = ret.cards.length;
2462             //Roo.log(this.items[i].el.dom.id);
2463             ret.cards.push(this.items[i]);
2464             last_card_n  = i;
2465             if (ret.card_n < 0 && pos == 'above') {
2466                 ret.position = cards_len > 0 ? 'below' : pos;
2467                 ret.items_n = i > 0 ? i - 1 : 0;
2468                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2469                 ret.card = ret.cards[ret.card_n];
2470             }
2471         }
2472         if (!ret.cards.length) {
2473             ret.card = true;
2474             ret.position = 'below';
2475             ret.items_n;
2476             return ret;
2477         }
2478         // could not find a card.. stick it at the end..
2479         if (ret.card_n < 0) {
2480             ret.card_n = last_card_n;
2481             ret.card = ret.cards[last_card_n];
2482             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2483             ret.position = 'below';
2484         }
2485         
2486         if (this.items[ret.items_n].el == dragged_card_el) {
2487             return false;
2488         }
2489         
2490         if (ret.position == 'below') {
2491             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2492             
2493             if (card_after  && card_after.el == dragged_card_el) {
2494                 return false;
2495             }
2496             return ret;
2497         }
2498         
2499         // its's after ..
2500         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2501         
2502         if (card_before  && card_before.el == dragged_card_el) {
2503             return false;
2504         }
2505         
2506         return ret;
2507     },
2508     
2509     onNodeEnter : function(n, dd, e, data){
2510         return false;
2511     },
2512     onNodeOver : function(n, dd, e, data)
2513     {
2514        
2515         var target_info = this.getTargetFromEvent(e,data.source.el);
2516         if (target_info === false) {
2517             this.dropPlaceHolder('hide');
2518             return false;
2519         }
2520         Roo.log(['getTargetFromEvent', target_info ]);
2521         
2522         
2523         if (this.fireEvent('cardover', this, [ data ]) === false) {
2524             return false;
2525         }
2526         
2527         this.dropPlaceHolder('show', target_info,data);
2528         
2529         return false; 
2530     },
2531     onNodeOut : function(n, dd, e, data){
2532         this.dropPlaceHolder('hide');
2533      
2534     },
2535     onNodeDrop : function(n, dd, e, data)
2536     {
2537         
2538         // call drop - return false if
2539         
2540         // this could actually fail - if the Network drops..
2541         // we will ignore this at present..- client should probably reload
2542         // the whole set of cards if stuff like that fails.
2543         
2544         
2545         var info = this.getTargetFromEvent(e,data.source.el);
2546         if (info === false) {
2547             return false;
2548         }
2549         this.dropPlaceHolder('hide');
2550   
2551           
2552     
2553         this.acceptCard(data.source, info.position, info.card, info.items_n);
2554         return true;
2555          
2556     },
2557     firstChildCard : function()
2558     {
2559         for (var i = 0;i< this.items.length;i++) {
2560             
2561             if (!this.items[i].el.hasClass('card')) {
2562                  continue;
2563             }
2564             return this.items[i];
2565         }
2566         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2567     },
2568     /**
2569      * accept card
2570      *
2571      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2572      */
2573     acceptCard : function(move_card,  position, next_to_card )
2574     {
2575         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2576             return false;
2577         }
2578         
2579         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2580         
2581         move_card.parent().removeCard(move_card);
2582         
2583         
2584         var dom = move_card.el.dom;
2585         dom.style.width = ''; // clear with - which is set by drag.
2586         
2587         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2588             var cardel = next_to_card.el.dom;
2589             
2590             if (position == 'above' ) {
2591                 cardel.parentNode.insertBefore(dom, cardel);
2592             } else if (cardel.nextSibling) {
2593                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2594             } else {
2595                 cardel.parentNode.append(dom);
2596             }
2597         } else {
2598             // card container???
2599             this.containerEl.dom.append(dom);
2600         }
2601         
2602         //FIXME HANDLE card = true 
2603         
2604         // add this to the correct place in items.
2605         
2606         // remove Card from items.
2607         
2608        
2609         if (this.items.length) {
2610             var nitems = [];
2611             //Roo.log([info.items_n, info.position, this.items.length]);
2612             for (var i =0; i < this.items.length; i++) {
2613                 if (i == to_items_n && position == 'above') {
2614                     nitems.push(move_card);
2615                 }
2616                 nitems.push(this.items[i]);
2617                 if (i == to_items_n && position == 'below') {
2618                     nitems.push(move_card);
2619                 }
2620             }
2621             this.items = nitems;
2622             Roo.log(this.items);
2623         } else {
2624             this.items.push(move_card);
2625         }
2626         
2627         move_card.parentId = this.id;
2628         
2629         return true;
2630         
2631         
2632     },
2633     removeCard : function(c)
2634     {
2635         this.items = this.items.filter(function(e) { return e != c });
2636  
2637         var dom = c.el.dom;
2638         dom.parentNode.removeChild(dom);
2639         dom.style.width = ''; // clear with - which is set by drag.
2640         c.parentId = false;
2641         
2642     },
2643     
2644     /**    Decide whether to drop above or below a View node. */
2645     getDropPoint : function(e, n, dd)
2646     {
2647         if (dd) {
2648              return false;
2649         }
2650         if (n == this.containerEl.dom) {
2651             return "above";
2652         }
2653         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2654         var c = t + (b - t) / 2;
2655         var y = Roo.lib.Event.getPageY(e);
2656         if(y <= c) {
2657             return "above";
2658         }else{
2659             return "below";
2660         }
2661     },
2662     onToggleCollapse : function(e)
2663         {
2664         if (this.collapsed) {
2665             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2666             this.collapsableEl.addClass('show');
2667             this.collapsed = false;
2668             return;
2669         }
2670         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2671         this.collapsableEl.removeClass('show');
2672         this.collapsed = true;
2673         
2674     
2675     },
2676     
2677     onToggleRotate : function(e)
2678     {
2679         this.collapsableEl.removeClass('show');
2680         this.footerEl.removeClass('d-none');
2681         this.el.removeClass('roo-card-rotated');
2682         this.el.removeClass('d-none');
2683         if (this.rotated) {
2684             
2685             this.collapsableEl.addClass('show');
2686             this.rotated = false;
2687             this.fireEvent('rotate', this, this.rotated);
2688             return;
2689         }
2690         this.el.addClass('roo-card-rotated');
2691         this.footerEl.addClass('d-none');
2692         this.el.select('.roo-collapsable').removeClass('show');
2693         
2694         this.rotated = true;
2695         this.fireEvent('rotate', this, this.rotated);
2696     
2697     },
2698     
2699     dropPlaceHolder: function (action, info, data)
2700     {
2701         if (this.dropEl === false) {
2702             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2703             cls : 'd-none'
2704             },true);
2705         }
2706         this.dropEl.removeClass(['d-none', 'd-block']);        
2707         if (action == 'hide') {
2708             
2709             this.dropEl.addClass('d-none');
2710             return;
2711         }
2712         // FIXME - info.card == true!!!
2713         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2714         
2715         if (info.card !== true) {
2716             var cardel = info.card.el.dom;
2717             
2718             if (info.position == 'above') {
2719                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2720             } else if (cardel.nextSibling) {
2721                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2722             } else {
2723                 cardel.parentNode.append(this.dropEl.dom);
2724             }
2725         } else {
2726             // card container???
2727             this.containerEl.dom.append(this.dropEl.dom);
2728         }
2729         
2730         this.dropEl.addClass('d-block roo-card-dropzone');
2731         
2732         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2733         
2734         
2735     
2736     
2737     
2738     },
2739     setHeaderText: function(html)
2740     {
2741         this.header = html;
2742         if (this.headerContainerEl) {
2743             this.headerContainerEl.dom.innerHTML = html;
2744         }
2745     },
2746     onHeaderImageLoad : function(ev, he)
2747     {
2748         if (!this.header_image_fit_square) {
2749             return;
2750         }
2751         
2752         var hw = he.naturalHeight / he.naturalWidth;
2753         // wide image = < 0
2754         // tall image = > 1
2755         //var w = he.dom.naturalWidth;
2756         var ww = he.width;
2757         he.style.left =  0;
2758         he.style.position =  'relative';
2759         if (hw > 1) {
2760             var nw = (ww * (1/hw));
2761             Roo.get(he).setSize( ww * (1/hw),  ww);
2762             he.style.left =  ((ww - nw)/ 2) + 'px';
2763             he.style.position =  'relative';
2764         }
2765
2766     }
2767
2768     
2769 });
2770
2771 /*
2772  * - LGPL
2773  *
2774  * Card header - holder for the card header elements.
2775  * 
2776  */
2777
2778 /**
2779  * @class Roo.bootstrap.CardHeader
2780  * @extends Roo.bootstrap.Element
2781  * @parent Roo.bootstrap.Card
2782  * @children Roo.bootstrap.Component
2783  * Bootstrap CardHeader class
2784  * @constructor
2785  * Create a new Card Header - that you can embed children into
2786  * @param {Object} config The config object
2787  */
2788
2789 Roo.bootstrap.CardHeader = function(config){
2790     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2791 };
2792
2793 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2794     
2795     
2796     container_method : 'getCardHeader' 
2797     
2798      
2799     
2800     
2801    
2802 });
2803
2804  
2805
2806  /*
2807  * - LGPL
2808  *
2809  * Card footer - holder for the card footer elements.
2810  * 
2811  */
2812
2813 /**
2814  * @class Roo.bootstrap.CardFooter
2815  * @extends Roo.bootstrap.Element
2816  * @parent Roo.bootstrap.Card
2817  * @children Roo.bootstrap.Component
2818  * Bootstrap CardFooter class
2819  * 
2820  * @constructor
2821  * Create a new Card Footer - that you can embed children into
2822  * @param {Object} config The config object
2823  */
2824
2825 Roo.bootstrap.CardFooter = function(config){
2826     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2827 };
2828
2829 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2830     
2831     
2832     container_method : 'getCardFooter' 
2833     
2834      
2835     
2836     
2837    
2838 });
2839
2840  
2841
2842  /*
2843  * - LGPL
2844  *
2845  * Card header - holder for the card header elements.
2846  * 
2847  */
2848
2849 /**
2850  * @class Roo.bootstrap.CardImageTop
2851  * @extends Roo.bootstrap.Element
2852  * @parent Roo.bootstrap.Card
2853  * @children Roo.bootstrap.Component
2854  * Bootstrap CardImageTop class
2855  * 
2856  * @constructor
2857  * Create a new Card Image Top container
2858  * @param {Object} config The config object
2859  */
2860
2861 Roo.bootstrap.CardImageTop = function(config){
2862     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2863 };
2864
2865 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2866     
2867    
2868     container_method : 'getCardImageTop' 
2869     
2870      
2871     
2872    
2873 });
2874
2875  
2876
2877  
2878 /*
2879 * Licence: LGPL
2880 */
2881
2882 /**
2883  * @class Roo.bootstrap.ButtonUploader
2884  * @extends Roo.bootstrap.Button
2885  * Bootstrap Button Uploader class - it's a button which when you add files to it
2886  *
2887  * 
2888  * @cfg {Number} errorTimeout default 3000
2889  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2890  * @cfg {Array}  html The button text.
2891  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2892  *
2893  * @constructor
2894  * Create a new CardUploader
2895  * @param {Object} config The config object
2896  */
2897
2898 Roo.bootstrap.ButtonUploader = function(config){
2899     
2900  
2901     
2902     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2903     
2904      
2905      this.addEvents({
2906          // raw events
2907         /**
2908          * @event beforeselect
2909          * When button is pressed, before show upload files dialog is shown
2910          * @param {Roo.bootstrap.UploaderButton} this
2911          *
2912          */
2913         'beforeselect' : true,
2914          /**
2915          * @event fired when files have been selected, 
2916          * When a the download link is clicked
2917          * @param {Roo.bootstrap.UploaderButton} this
2918          * @param {Array} Array of files that have been uploaded
2919          */
2920         'uploaded' : true
2921         
2922     });
2923 };
2924  
2925 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2926     
2927      
2928     errorTimeout : 3000,
2929      
2930     images : false,
2931    
2932     fileCollection : false,
2933     allowBlank : true,
2934     
2935     multiple : true,
2936     
2937     getAutoCreate : function()
2938     {
2939        
2940         
2941         return  {
2942             cls :'div' ,
2943             cn : [
2944                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this) 
2945             ]
2946         };
2947            
2948          
2949     },
2950      
2951    
2952     initEvents : function()
2953     {
2954         
2955         Roo.bootstrap.Button.prototype.initEvents.call(this);
2956         
2957         
2958         
2959         
2960         
2961         this.urlAPI = (window.createObjectURL && window) || 
2962                                 (window.URL && URL.revokeObjectURL && URL) || 
2963                                 (window.webkitURL && webkitURL);
2964                         
2965         var im = {
2966             tag: 'input',
2967             type : 'file',
2968             cls : 'd-none  roo-card-upload-selector' 
2969           
2970         };
2971         if (this.multiple) {
2972             im.multiple = 'multiple';
2973         }
2974         this.selectorEl = Roo.get(document.body).createChild(im); // so it does not capture click event for navitem.
2975        
2976         //this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2977         
2978         this.selectorEl.on('change', this.onFileSelected, this);
2979          
2980          
2981        
2982     },
2983     
2984    
2985     onClick : function(e)
2986     {
2987         e.preventDefault();
2988         
2989         if ( this.fireEvent('beforeselect', this) === false) {
2990             return;
2991         }
2992          
2993         this.selectorEl.dom.click();
2994          
2995     },
2996     
2997     onFileSelected : function(e)
2998     {
2999         e.preventDefault();
3000         
3001         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3002             return;
3003         }
3004         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3005         this.selectorEl.dom.value  = '';// hopefully reset..
3006         
3007         this.fireEvent('uploaded', this,  files );
3008         
3009     },
3010     
3011        
3012    
3013     
3014     /**
3015      * addCard - add an Attachment to the uploader
3016      * @param data - the data about the image to upload
3017      *
3018      * {
3019           id : 123
3020           title : "Title of file",
3021           is_uploaded : false,
3022           src : "http://.....",
3023           srcfile : { the File upload object },
3024           mimetype : file.type,
3025           preview : false,
3026           is_deleted : 0
3027           .. any other data...
3028         }
3029      *
3030      * 
3031     */
3032      
3033     reset: function()
3034     {
3035          
3036          this.selectorEl
3037     } 
3038     
3039     
3040     
3041     
3042 });
3043  /*
3044  * - LGPL
3045  *
3046  * image
3047  * 
3048  */
3049
3050
3051 /**
3052  * @class Roo.bootstrap.Img
3053  * @extends Roo.bootstrap.Component
3054  * Bootstrap Img class
3055  * @cfg {Boolean} imgResponsive false | true
3056  * @cfg {String} border rounded | circle | thumbnail
3057  * @cfg {String} src image source
3058  * @cfg {String} alt image alternative text
3059  * @cfg {String} href a tag href
3060  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3061  * @cfg {String} xsUrl xs image source
3062  * @cfg {String} smUrl sm image source
3063  * @cfg {String} mdUrl md image source
3064  * @cfg {String} lgUrl lg image source
3065  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3066  * 
3067  * @constructor
3068  * Create a new Input
3069  * @param {Object} config The config object
3070  */
3071
3072 Roo.bootstrap.Img = function(config){
3073     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3074     
3075     this.addEvents({
3076         // img events
3077         /**
3078          * @event click
3079          * The img click event for the img.
3080          * @param {Roo.EventObject} e
3081          */
3082         "click" : true,
3083         /**
3084          * @event load
3085          * The when any image loads
3086          * @param {Roo.EventObject} e
3087          */
3088         "load" : true
3089     });
3090 };
3091
3092 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3093     
3094     imgResponsive: true,
3095     border: '',
3096     src: 'about:blank',
3097     href: false,
3098     target: false,
3099     xsUrl: '',
3100     smUrl: '',
3101     mdUrl: '',
3102     lgUrl: '',
3103     backgroundContain : false,
3104
3105     getAutoCreate : function()
3106     {   
3107         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3108             return this.createSingleImg();
3109         }
3110         
3111         var cfg = {
3112             tag: 'div',
3113             cls: 'roo-image-responsive-group',
3114             cn: []
3115         };
3116         var _this = this;
3117         
3118         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3119             
3120             if(!_this[size + 'Url']){
3121                 return;
3122             }
3123             
3124             var img = {
3125                 tag: 'img',
3126                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3127                 html: _this.html || cfg.html,
3128                 src: _this[size + 'Url']
3129             };
3130             
3131             img.cls += ' roo-image-responsive-' + size;
3132             
3133             var s = ['xs', 'sm', 'md', 'lg'];
3134             
3135             s.splice(s.indexOf(size), 1);
3136             
3137             Roo.each(s, function(ss){
3138                 img.cls += ' hidden-' + ss;
3139             });
3140             
3141             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3142                 cfg.cls += ' img-' + _this.border;
3143             }
3144             
3145             if(_this.alt){
3146                 cfg.alt = _this.alt;
3147             }
3148             
3149             if(_this.href){
3150                 var a = {
3151                     tag: 'a',
3152                     href: _this.href,
3153                     cn: [
3154                         img
3155                     ]
3156                 };
3157
3158                 if(this.target){
3159                     a.target = _this.target;
3160                 }
3161             }
3162             
3163             cfg.cn.push((_this.href) ? a : img);
3164             
3165         });
3166         
3167         return cfg;
3168     },
3169     
3170     createSingleImg : function()
3171     {
3172         var cfg = {
3173             tag: 'img',
3174             cls: (this.imgResponsive) ? 'img-responsive' : '',
3175             html : null,
3176             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3177         };
3178         
3179         if (this.backgroundContain) {
3180             cfg.cls += ' background-contain';
3181         }
3182         
3183         cfg.html = this.html || cfg.html;
3184         
3185         if (this.backgroundContain) {
3186             cfg.style="background-image: url(" + this.src + ')';
3187         } else {
3188             cfg.src = this.src || cfg.src;
3189         }
3190         
3191         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3192             cfg.cls += ' img-' + this.border;
3193         }
3194         
3195         if(this.alt){
3196             cfg.alt = this.alt;
3197         }
3198         
3199         if(this.href){
3200             var a = {
3201                 tag: 'a',
3202                 href: this.href,
3203                 cn: [
3204                     cfg
3205                 ]
3206             };
3207             
3208             if(this.target){
3209                 a.target = this.target;
3210             }
3211             
3212         }
3213         
3214         return (this.href) ? a : cfg;
3215     },
3216     
3217     initEvents: function() 
3218     {
3219         if(!this.href){
3220             this.el.on('click', this.onClick, this);
3221         }
3222         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3223             this.el.on('load', this.onImageLoad, this);
3224         } else {
3225             // not sure if this works.. not tested
3226             this.el.select('img', true).on('load', this.onImageLoad, this);
3227         }
3228         
3229     },
3230     
3231     onClick : function(e)
3232     {
3233         Roo.log('img onclick');
3234         this.fireEvent('click', this, e);
3235     },
3236     onImageLoad: function(e)
3237     {
3238         Roo.log('img load');
3239         this.fireEvent('load', this, e);
3240     },
3241     
3242     /**
3243      * Sets the url of the image - used to update it
3244      * @param {String} url the url of the image
3245      */
3246     
3247     setSrc : function(url)
3248     {
3249         this.src =  url;
3250         
3251         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3252             if (this.backgroundContain) {
3253                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3254             } else {
3255                 this.el.dom.src =  url;
3256             }
3257             return;
3258         }
3259         
3260         this.el.select('img', true).first().dom.src =  url;
3261     }
3262     
3263     
3264    
3265 });
3266
3267  /*
3268  * - LGPL
3269  *
3270  * image
3271  * 
3272  */
3273
3274
3275 /**
3276  * @class Roo.bootstrap.Link
3277  * @extends Roo.bootstrap.Component
3278  * @children Roo.bootstrap.Component
3279  * Bootstrap Link Class (eg. '<a href>')
3280  
3281  * @cfg {String} alt image alternative text
3282  * @cfg {String} href a tag href
3283  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3284  * @cfg {String} html the content of the link.
3285  * @cfg {String} anchor name for the anchor link
3286  * @cfg {String} fa - favicon
3287
3288  * @cfg {Boolean} preventDefault (true | false) default false
3289
3290  * 
3291  * @constructor
3292  * Create a new Input
3293  * @param {Object} config The config object
3294  */
3295
3296 Roo.bootstrap.Link = function(config){
3297     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3298     
3299     this.addEvents({
3300         // img events
3301         /**
3302          * @event click
3303          * The img click event for the img.
3304          * @param {Roo.EventObject} e
3305          */
3306         "click" : true
3307     });
3308 };
3309
3310 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3311     
3312     href: false,
3313     target: false,
3314     preventDefault: false,
3315     anchor : false,
3316     alt : false,
3317     fa: false,
3318
3319
3320     getAutoCreate : function()
3321     {
3322         var html = this.html || '';
3323         
3324         if (this.fa !== false) {
3325             html = '<i class="fa fa-' + this.fa + '"></i>';
3326         }
3327         var cfg = {
3328             tag: 'a'
3329         };
3330         // anchor's do not require html/href...
3331         if (this.anchor === false) {
3332             cfg.html = html;
3333             cfg.href = this.href || '#';
3334         } else {
3335             cfg.name = this.anchor;
3336             if (this.html !== false || this.fa !== false) {
3337                 cfg.html = html;
3338             }
3339             if (this.href !== false) {
3340                 cfg.href = this.href;
3341             }
3342         }
3343         
3344         if(this.alt !== false){
3345             cfg.alt = this.alt;
3346         }
3347         
3348         
3349         if(this.target !== false) {
3350             cfg.target = this.target;
3351         }
3352         
3353         return cfg;
3354     },
3355     
3356     initEvents: function() {
3357         
3358         if(!this.href || this.preventDefault){
3359             this.el.on('click', this.onClick, this);
3360         }
3361     },
3362     
3363     onClick : function(e)
3364     {
3365         if(this.preventDefault){
3366             e.preventDefault();
3367         }
3368         //Roo.log('img onclick');
3369         this.fireEvent('click', this, e);
3370     }
3371    
3372 });
3373
3374  /*
3375  * - LGPL
3376  *
3377  * header
3378  * 
3379  */
3380
3381 /**
3382  * @class Roo.bootstrap.Header
3383  * @extends Roo.bootstrap.Component
3384  * @children Roo.bootstrap.Component
3385  * Bootstrap Header class
3386  *
3387  * 
3388  * @cfg {String} html content of header
3389  * @cfg {Number} level (1|2|3|4|5|6) default 1
3390  * 
3391  * @constructor
3392  * Create a new Header
3393  * @param {Object} config The config object
3394  */
3395
3396
3397 Roo.bootstrap.Header  = function(config){
3398     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3399 };
3400
3401 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3402     
3403     //href : false,
3404     html : false,
3405     level : 1,
3406     
3407     
3408     
3409     getAutoCreate : function(){
3410         
3411         
3412         
3413         var cfg = {
3414             tag: 'h' + (1 *this.level),
3415             html: this.html || ''
3416         } ;
3417         
3418         return cfg;
3419     }
3420    
3421 });
3422
3423  
3424
3425  /**
3426  * @class Roo.bootstrap.MenuMgr
3427  * @licence LGPL
3428  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3429  * @static
3430  */
3431 Roo.bootstrap.menu.Manager = function(){
3432    var menus, active, groups = {}, attached = false, lastShow = new Date();
3433
3434    // private - called when first menu is created
3435    function init(){
3436        menus = {};
3437        active = new Roo.util.MixedCollection();
3438        Roo.get(document).addKeyListener(27, function(){
3439            if(active.length > 0){
3440                hideAll();
3441            }
3442        });
3443    }
3444
3445    // private
3446    function hideAll(){
3447        if(active && active.length > 0){
3448            var c = active.clone();
3449            c.each(function(m){
3450                m.hide();
3451            });
3452        }
3453    }
3454
3455    // private
3456    function onHide(m){
3457        active.remove(m);
3458        if(active.length < 1){
3459            Roo.get(document).un("mouseup", onMouseDown);
3460             
3461            attached = false;
3462        }
3463    }
3464
3465    // private
3466    function onShow(m){
3467        var last = active.last();
3468        lastShow = new Date();
3469        active.add(m);
3470        if(!attached){
3471           Roo.get(document).on("mouseup", onMouseDown);
3472            
3473            attached = true;
3474        }
3475        if(m.parentMenu){
3476           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3477           m.parentMenu.activeChild = m;
3478        }else if(last && last.isVisible()){
3479           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3480        }
3481    }
3482
3483    // private
3484    function onBeforeHide(m){
3485        if(m.activeChild){
3486            m.activeChild.hide();
3487        }
3488        if(m.autoHideTimer){
3489            clearTimeout(m.autoHideTimer);
3490            delete m.autoHideTimer;
3491        }
3492    }
3493
3494    // private
3495    function onBeforeShow(m){
3496        var pm = m.parentMenu;
3497        if(!pm && !m.allowOtherMenus){
3498            hideAll();
3499        }else if(pm && pm.activeChild && active != m){
3500            pm.activeChild.hide();
3501        }
3502    }
3503
3504    // private this should really trigger on mouseup..
3505    function onMouseDown(e){
3506         Roo.log("on Mouse Up");
3507         
3508         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3509             Roo.log("MenuManager hideAll");
3510             hideAll();
3511             e.stopEvent();
3512         }
3513         
3514         
3515    }
3516
3517    // private
3518    function onBeforeCheck(mi, state){
3519        if(state){
3520            var g = groups[mi.group];
3521            for(var i = 0, l = g.length; i < l; i++){
3522                if(g[i] != mi){
3523                    g[i].setChecked(false);
3524                }
3525            }
3526        }
3527    }
3528
3529    return {
3530
3531        /**
3532         * Hides all menus that are currently visible
3533         */
3534        hideAll : function(){
3535             hideAll();  
3536        },
3537
3538        // private
3539        register : function(menu){
3540            if(!menus){
3541                init();
3542            }
3543            menus[menu.id] = menu;
3544            menu.on("beforehide", onBeforeHide);
3545            menu.on("hide", onHide);
3546            menu.on("beforeshow", onBeforeShow);
3547            menu.on("show", onShow);
3548            var g = menu.group;
3549            if(g && menu.events["checkchange"]){
3550                if(!groups[g]){
3551                    groups[g] = [];
3552                }
3553                groups[g].push(menu);
3554                menu.on("checkchange", onCheck);
3555            }
3556        },
3557
3558         /**
3559          * Returns a {@link Roo.menu.Menu} object
3560          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3561          * be used to generate and return a new Menu instance.
3562          */
3563        get : function(menu){
3564            if(typeof menu == "string"){ // menu id
3565                return menus[menu];
3566            }else if(menu.events){  // menu instance
3567                return menu;
3568            }
3569            /*else if(typeof menu.length == 'number'){ // array of menu items?
3570                return new Roo.bootstrap.Menu({items:menu});
3571            }else{ // otherwise, must be a config
3572                return new Roo.bootstrap.Menu(menu);
3573            }
3574            */
3575            return false;
3576        },
3577
3578        // private
3579        unregister : function(menu){
3580            delete menus[menu.id];
3581            menu.un("beforehide", onBeforeHide);
3582            menu.un("hide", onHide);
3583            menu.un("beforeshow", onBeforeShow);
3584            menu.un("show", onShow);
3585            var g = menu.group;
3586            if(g && menu.events["checkchange"]){
3587                groups[g].remove(menu);
3588                menu.un("checkchange", onCheck);
3589            }
3590        },
3591
3592        // private
3593        registerCheckable : function(menuItem){
3594            var g = menuItem.group;
3595            if(g){
3596                if(!groups[g]){
3597                    groups[g] = [];
3598                }
3599                groups[g].push(menuItem);
3600                menuItem.on("beforecheckchange", onBeforeCheck);
3601            }
3602        },
3603
3604        // private
3605        unregisterCheckable : function(menuItem){
3606            var g = menuItem.group;
3607            if(g){
3608                groups[g].remove(menuItem);
3609                menuItem.un("beforecheckchange", onBeforeCheck);
3610            }
3611        }
3612    };
3613 }(); 
3614 /**
3615  * @class Roo.bootstrap.menu.Menu
3616  * @extends Roo.bootstrap.Component
3617  * @licence LGPL
3618  * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3619  * @parent none
3620  * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3621  * 
3622  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3623  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3624  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3625  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3626 * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3627 * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3628  
3629  * @constructor
3630  * Create a new Menu
3631  * @param {Object} config The config objectQ
3632  */
3633
3634
3635 Roo.bootstrap.menu.Menu = function(config){
3636     
3637     if (config.type == 'treeview') {
3638         // normally menu's are drawn attached to the document to handle layering etc..
3639         // however treeview (used by the docs menu is drawn into the parent element)
3640         this.container_method = 'getChildContainer'; 
3641     }
3642     
3643     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3644     if (this.registerMenu && this.type != 'treeview')  {
3645         Roo.bootstrap.menu.Manager.register(this);
3646     }
3647     
3648     
3649     this.addEvents({
3650         /**
3651          * @event beforeshow
3652          * Fires before this menu is displayed (return false to block)
3653          * @param {Roo.menu.Menu} this
3654          */
3655         beforeshow : true,
3656         /**
3657          * @event beforehide
3658          * Fires before this menu is hidden (return false to block)
3659          * @param {Roo.menu.Menu} this
3660          */
3661         beforehide : true,
3662         /**
3663          * @event show
3664          * Fires after this menu is displayed
3665          * @param {Roo.menu.Menu} this
3666          */
3667         show : true,
3668         /**
3669          * @event hide
3670          * Fires after this menu is hidden
3671          * @param {Roo.menu.Menu} this
3672          */
3673         hide : true,
3674         /**
3675          * @event click
3676          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3677          * @param {Roo.menu.Menu} this
3678          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3679          * @param {Roo.EventObject} e
3680          */
3681         click : true,
3682         /**
3683          * @event mouseover
3684          * Fires when the mouse is hovering over this menu
3685          * @param {Roo.menu.Menu} this
3686          * @param {Roo.EventObject} e
3687          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3688          */
3689         mouseover : true,
3690         /**
3691          * @event mouseout
3692          * Fires when the mouse exits this menu
3693          * @param {Roo.menu.Menu} this
3694          * @param {Roo.EventObject} e
3695          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3696          */
3697         mouseout : true,
3698         /**
3699          * @event itemclick
3700          * Fires when a menu item contained in this menu is clicked
3701          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3702          * @param {Roo.EventObject} e
3703          */
3704         itemclick: true
3705     });
3706     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3707 };
3708
3709 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
3710     
3711    /// html : false,
3712    
3713     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3714     type: false,
3715     /**
3716      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3717      */
3718     registerMenu : true,
3719     
3720     menuItems :false, // stores the menu items..
3721     
3722     hidden:true,
3723         
3724     parentMenu : false,
3725     
3726     stopEvent : true,
3727     
3728     isLink : false,
3729     
3730     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3731     
3732     hideTrigger : false,
3733     
3734     align : 'tl-bl?',
3735     
3736     
3737     getChildContainer : function() {
3738         return this.el;  
3739     },
3740     
3741     getAutoCreate : function(){
3742          
3743         //if (['right'].indexOf(this.align)!==-1) {
3744         //    cfg.cn[1].cls += ' pull-right'
3745         //}
3746          
3747         var cfg = {
3748             tag : 'ul',
3749             cls : 'dropdown-menu shadow' ,
3750             style : 'z-index:1000'
3751             
3752         };
3753         
3754         if (this.type === 'submenu') {
3755             cfg.cls = 'submenu active';
3756         }
3757         if (this.type === 'treeview') {
3758             cfg.cls = 'treeview-menu';
3759         }
3760         
3761         return cfg;
3762     },
3763     initEvents : function() {
3764         
3765        // Roo.log("ADD event");
3766        // Roo.log(this.triggerEl.dom);
3767         if (this.triggerEl) {
3768             
3769             this.triggerEl.on('click', this.onTriggerClick, this);
3770             
3771             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3772             
3773             if (!this.hideTrigger) {
3774                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3775                     // dropdown toggle on the 'a' in BS4?
3776                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3777                 } else {
3778                     this.triggerEl.addClass('dropdown-toggle');
3779                 }
3780             }
3781         }
3782         
3783         if (Roo.isTouch) {
3784             this.el.on('touchstart'  , this.onTouch, this);
3785         }
3786         this.el.on('click' , this.onClick, this);
3787
3788         this.el.on("mouseover", this.onMouseOver, this);
3789         this.el.on("mouseout", this.onMouseOut, this);
3790         
3791     },
3792     
3793     findTargetItem : function(e)
3794     {
3795         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3796         if(!t){
3797             return false;
3798         }
3799         //Roo.log(t);         Roo.log(t.id);
3800         if(t && t.id){
3801             //Roo.log(this.menuitems);
3802             return this.menuitems.get(t.id);
3803             
3804             //return this.items.get(t.menuItemId);
3805         }
3806         
3807         return false;
3808     },
3809     
3810     onTouch : function(e) 
3811     {
3812         Roo.log("menu.onTouch");
3813         //e.stopEvent(); this make the user popdown broken
3814         this.onClick(e);
3815     },
3816     
3817     onClick : function(e)
3818     {
3819         Roo.log("menu.onClick");
3820         
3821         var t = this.findTargetItem(e);
3822         if(!t || t.isContainer){
3823             return;
3824         }
3825         Roo.log(e);
3826         /*
3827         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3828             if(t == this.activeItem && t.shouldDeactivate(e)){
3829                 this.activeItem.deactivate();
3830                 delete this.activeItem;
3831                 return;
3832             }
3833             if(t.canActivate){
3834                 this.setActiveItem(t, true);
3835             }
3836             return;
3837             
3838             
3839         }
3840         */
3841        
3842         Roo.log('pass click event');
3843         
3844         t.onClick(e);
3845         
3846         this.fireEvent("click", this, t, e);
3847         
3848         var _this = this;
3849         
3850         if(!t.href.length || t.href == '#'){
3851             (function() { _this.hide(); }).defer(100);
3852         }
3853         
3854     },
3855     
3856     onMouseOver : function(e){
3857         var t  = this.findTargetItem(e);
3858         //Roo.log(t);
3859         //if(t){
3860         //    if(t.canActivate && !t.disabled){
3861         //        this.setActiveItem(t, true);
3862         //    }
3863         //}
3864         
3865         this.fireEvent("mouseover", this, e, t);
3866     },
3867     isVisible : function(){
3868         return !this.hidden;
3869     },
3870     onMouseOut : function(e){
3871         var t  = this.findTargetItem(e);
3872         
3873         //if(t ){
3874         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3875         //        this.activeItem.deactivate();
3876         //        delete this.activeItem;
3877         //    }
3878         //}
3879         this.fireEvent("mouseout", this, e, t);
3880     },
3881     
3882     
3883     /**
3884      * Displays this menu relative to another element
3885      * @param {String/HTMLElement/Roo.Element} element The element to align to
3886      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3887      * the element (defaults to this.defaultAlign)
3888      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3889      */
3890     show : function(el, pos, parentMenu)
3891     {
3892         if (false === this.fireEvent("beforeshow", this)) {
3893             Roo.log("show canceled");
3894             return;
3895         }
3896         this.parentMenu = parentMenu;
3897         if(!this.el){
3898             this.render();
3899         }
3900         this.el.addClass('show'); // show otherwise we do not know how big we are..
3901          
3902         var xy = this.el.getAlignToXY(el, pos);
3903         
3904         // bl-tl << left align  below
3905         // tl-bl << left align 
3906         
3907         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3908             // if it goes to far to the right.. -> align left.
3909             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3910         }
3911         if(xy[0] < 0){
3912             // was left align - go right?
3913             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3914         }
3915         
3916         // goes down the bottom
3917         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3918            xy[1]  < 0 ){
3919             var a = this.align.replace('?', '').split('-');
3920             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3921             
3922         }
3923         
3924         this.showAt(  xy , parentMenu, false);
3925     },
3926      /**
3927      * Displays this menu at a specific xy position
3928      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3929      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3930      */
3931     showAt : function(xy, parentMenu, /* private: */_e){
3932         this.parentMenu = parentMenu;
3933         if(!this.el){
3934             this.render();
3935         }
3936         if(_e !== false){
3937             this.fireEvent("beforeshow", this);
3938             //xy = this.el.adjustForConstraints(xy);
3939         }
3940         
3941         //this.el.show();
3942         this.hideMenuItems();
3943         this.hidden = false;
3944         if (this.triggerEl) {
3945             this.triggerEl.addClass('open');
3946         }
3947         
3948         this.el.addClass('show');
3949         
3950         
3951         
3952         // reassign x when hitting right
3953         
3954         // reassign y when hitting bottom
3955         
3956         // but the list may align on trigger left or trigger top... should it be a properity?
3957         
3958         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3959             this.el.setXY(xy);
3960         }
3961         
3962         this.focus();
3963         this.fireEvent("show", this);
3964     },
3965     
3966     focus : function(){
3967         return;
3968         if(!this.hidden){
3969             this.doFocus.defer(50, this);
3970         }
3971     },
3972
3973     doFocus : function(){
3974         if(!this.hidden){
3975             this.focusEl.focus();
3976         }
3977     },
3978
3979     /**
3980      * Hides this menu and optionally all parent menus
3981      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3982      */
3983     hide : function(deep)
3984     {
3985         if (false === this.fireEvent("beforehide", this)) {
3986             Roo.log("hide canceled");
3987             return;
3988         }
3989         this.hideMenuItems();
3990         if(this.el && this.isVisible()){
3991            
3992             if(this.activeItem){
3993                 this.activeItem.deactivate();
3994                 this.activeItem = null;
3995             }
3996             if (this.triggerEl) {
3997                 this.triggerEl.removeClass('open');
3998             }
3999             
4000             this.el.removeClass('show');
4001             this.hidden = true;
4002             this.fireEvent("hide", this);
4003         }
4004         if(deep === true && this.parentMenu){
4005             this.parentMenu.hide(true);
4006         }
4007     },
4008     
4009     onTriggerClick : function(e)
4010     {
4011         Roo.log('trigger click');
4012         
4013         var target = e.getTarget();
4014         
4015         Roo.log(target.nodeName.toLowerCase());
4016         
4017         if(target.nodeName.toLowerCase() === 'i'){
4018             e.preventDefault();
4019         }
4020         
4021     },
4022     
4023     onTriggerPress  : function(e)
4024     {
4025         Roo.log('trigger press');
4026         //Roo.log(e.getTarget());
4027        // Roo.log(this.triggerEl.dom);
4028        
4029         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4030         var pel = Roo.get(e.getTarget());
4031         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4032             Roo.log('is treeview or dropdown?');
4033             return;
4034         }
4035         
4036         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4037             return;
4038         }
4039         
4040         if (this.isVisible()) {
4041             Roo.log('hide');
4042             this.hide();
4043         } else {
4044             Roo.log('show');
4045             
4046             this.show(this.triggerEl, this.align, false);
4047         }
4048         
4049         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4050             e.stopEvent();
4051         }
4052         
4053     },
4054        
4055     
4056     hideMenuItems : function()
4057     {
4058         Roo.log("hide Menu Items");
4059         if (!this.el) { 
4060             return;
4061         }
4062         
4063         this.el.select('.open',true).each(function(aa) {
4064             
4065             aa.removeClass('open');
4066          
4067         });
4068     },
4069     addxtypeChild : function (tree, cntr) {
4070         var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4071           
4072         this.menuitems.add(comp);
4073         return comp;
4074
4075     },
4076     getEl : function()
4077     {
4078         Roo.log(this.el);
4079         return this.el;
4080     },
4081     
4082     clear : function()
4083     {
4084         this.getEl().dom.innerHTML = '';
4085         this.menuitems.clear();
4086     }
4087 });
4088
4089  
4090  /**
4091  * @class Roo.bootstrap.menu.Item
4092  * @extends Roo.bootstrap.Component
4093  * @children  Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4094  * @parent Roo.bootstrap.menu.Menu
4095  * @licence LGPL
4096  * Bootstrap MenuItem class
4097  * 
4098  * @cfg {String} html the menu label
4099  * @cfg {String} href the link
4100  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4101  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4102  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4103  * @cfg {String} fa favicon to show on left of menu item.
4104  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4105  * 
4106  * 
4107  * @constructor
4108  * Create a new MenuItem
4109  * @param {Object} config The config object
4110  */
4111
4112
4113 Roo.bootstrap.menu.Item = function(config){
4114     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4115     this.addEvents({
4116         // raw events
4117         /**
4118          * @event click
4119          * The raw click event for the entire grid.
4120          * @param {Roo.bootstrap.menu.Item} this
4121          * @param {Roo.EventObject} e
4122          */
4123         "click" : true
4124     });
4125 };
4126
4127 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
4128     
4129     href : false,
4130     html : false,
4131     preventDefault: false,
4132     isContainer : false,
4133     active : false,
4134     fa: false,
4135     
4136     getAutoCreate : function(){
4137         
4138         if(this.isContainer){
4139             return {
4140                 tag: 'li',
4141                 cls: 'dropdown-menu-item '
4142             };
4143         }
4144         var ctag = {
4145             tag: 'span',
4146             html: 'Link'
4147         };
4148         
4149         var anc = {
4150             tag : 'a',
4151             cls : 'dropdown-item',
4152             href : '#',
4153             cn : [  ]
4154         };
4155         
4156         if (this.fa !== false) {
4157             anc.cn.push({
4158                 tag : 'i',
4159                 cls : 'fa fa-' + this.fa
4160             });
4161         }
4162         
4163         anc.cn.push(ctag);
4164         
4165         
4166         var cfg= {
4167             tag: 'li',
4168             cls: 'dropdown-menu-item',
4169             cn: [ anc ]
4170         };
4171         if (this.parent().type == 'treeview') {
4172             cfg.cls = 'treeview-menu';
4173         }
4174         if (this.active) {
4175             cfg.cls += ' active';
4176         }
4177         
4178         
4179         
4180         anc.href = this.href || cfg.cn[0].href ;
4181         ctag.html = this.html || cfg.cn[0].html ;
4182         return cfg;
4183     },
4184     
4185     initEvents: function()
4186     {
4187         if (this.parent().type == 'treeview') {
4188             this.el.select('a').on('click', this.onClick, this);
4189         }
4190         
4191         if (this.menu) {
4192             this.menu.parentType = this.xtype;
4193             this.menu.triggerEl = this.el;
4194             this.menu = this.addxtype(Roo.apply({}, this.menu));
4195         }
4196         
4197     },
4198     onClick : function(e)
4199     {
4200         //Roo.log('item on click ');
4201         
4202         if(this.href === false || this.preventDefault){
4203             e.preventDefault();
4204         }
4205         //this.parent().hideMenuItems();
4206         
4207         this.fireEvent('click', this, e);
4208     },
4209     getEl : function()
4210     {
4211         return this.el;
4212     } 
4213 });
4214
4215  
4216
4217  
4218
4219   
4220 /**
4221  * @class Roo.bootstrap.menu.Separator
4222  * @extends Roo.bootstrap.Component
4223  * @licence LGPL
4224  * @parent Roo.bootstrap.menu.Menu
4225  * Bootstrap Separator class
4226  * 
4227  * @constructor
4228  * Create a new Separator
4229  * @param {Object} config The config object
4230  */
4231
4232
4233 Roo.bootstrap.menu.Separator = function(config){
4234     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4235 };
4236
4237 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
4238     
4239     getAutoCreate : function(){
4240         var cfg = {
4241             tag : 'li',
4242             cls: 'dropdown-divider divider'
4243         };
4244         
4245         return cfg;
4246     }
4247    
4248 });
4249
4250  
4251
4252  
4253 /*
4254 * Licence: LGPL
4255 */
4256
4257 /**
4258  * @class Roo.bootstrap.Modal
4259  * @extends Roo.bootstrap.Component
4260  * @parent none builder
4261  * @children Roo.bootstrap.Component
4262  * Bootstrap Modal class
4263  * @cfg {String} title Title of dialog
4264  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4265  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4266  * @cfg {Boolean} specificTitle default false
4267  * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4268  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4269  * @cfg {Boolean} animate default true
4270  * @cfg {Boolean} allow_close default true
4271  * @cfg {Boolean} fitwindow default false
4272  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4273  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4274  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4275  * @cfg {String} size (sm|lg|xl) default empty
4276  * @cfg {Number} max_width set the max width of modal
4277  * @cfg {Boolean} editableTitle can the title be edited
4278
4279  *
4280  *
4281  * @constructor
4282  * Create a new Modal Dialog
4283  * @param {Object} config The config object
4284  */
4285
4286 Roo.bootstrap.Modal = function(config){
4287     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4288     this.addEvents({
4289         // raw events
4290         /**
4291          * @event btnclick
4292          * The raw btnclick event for the button
4293          * @param {Roo.EventObject} e
4294          */
4295         "btnclick" : true,
4296         /**
4297          * @event resize
4298          * Fire when dialog resize
4299          * @param {Roo.bootstrap.Modal} this
4300          * @param {Roo.EventObject} e
4301          */
4302         "resize" : true,
4303         /**
4304          * @event titlechanged
4305          * Fire when the editable title has been changed
4306          * @param {Roo.bootstrap.Modal} this
4307          * @param {Roo.EventObject} value
4308          */
4309         "titlechanged" : true 
4310         
4311     });
4312     this.buttons = this.buttons || [];
4313
4314     if (this.tmpl) {
4315         this.tmpl = Roo.factory(this.tmpl);
4316     }
4317
4318 };
4319
4320 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4321
4322     title : 'test dialog',
4323
4324     buttons : false,
4325
4326     // set on load...
4327
4328     html: false,
4329
4330     tmp: false,
4331
4332     specificTitle: false,
4333
4334     buttonPosition: 'right',
4335
4336     allow_close : true,
4337
4338     animate : true,
4339
4340     fitwindow: false,
4341     
4342      // private
4343     dialogEl: false,
4344     bodyEl:  false,
4345     footerEl:  false,
4346     titleEl:  false,
4347     closeEl:  false,
4348
4349     size: '',
4350     
4351     max_width: 0,
4352     
4353     max_height: 0,
4354     
4355     fit_content: false,
4356     editableTitle  : false,
4357
4358     onRender : function(ct, position)
4359     {
4360         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4361
4362         if(!this.el){
4363             var cfg = Roo.apply({},  this.getAutoCreate());
4364             cfg.id = Roo.id();
4365             //if(!cfg.name){
4366             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4367             //}
4368             //if (!cfg.name.length) {
4369             //    delete cfg.name;
4370            // }
4371             if (this.cls) {
4372                 cfg.cls += ' ' + this.cls;
4373             }
4374             if (this.style) {
4375                 cfg.style = this.style;
4376             }
4377             this.el = Roo.get(document.body).createChild(cfg, position);
4378         }
4379         //var type = this.el.dom.type;
4380
4381
4382         if(this.tabIndex !== undefined){
4383             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4384         }
4385
4386         this.dialogEl = this.el.select('.modal-dialog',true).first();
4387         this.bodyEl = this.el.select('.modal-body',true).first();
4388         this.closeEl = this.el.select('.modal-header .close', true).first();
4389         this.headerEl = this.el.select('.modal-header',true).first();
4390         this.titleEl = this.el.select('.modal-title',true).first();
4391         this.footerEl = this.el.select('.modal-footer',true).first();
4392
4393         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4394         
4395         //this.el.addClass("x-dlg-modal");
4396
4397         if (this.buttons.length) {
4398             Roo.each(this.buttons, function(bb) {
4399                 var b = Roo.apply({}, bb);
4400                 b.xns = b.xns || Roo.bootstrap;
4401                 b.xtype = b.xtype || 'Button';
4402                 if (typeof(b.listeners) == 'undefined') {
4403                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4404                 }
4405
4406                 var btn = Roo.factory(b);
4407
4408                 btn.render(this.getButtonContainer());
4409
4410             },this);
4411         }
4412         // render the children.
4413         var nitems = [];
4414
4415         if(typeof(this.items) != 'undefined'){
4416             var items = this.items;
4417             delete this.items;
4418
4419             for(var i =0;i < items.length;i++) {
4420                 // we force children not to montor widnow resize  - as we do that for them.
4421                 items[i].monitorWindowResize = false;
4422                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4423             }
4424         }
4425
4426         this.items = nitems;
4427
4428         // where are these used - they used to be body/close/footer
4429
4430
4431         this.initEvents();
4432         //this.el.addClass([this.fieldClass, this.cls]);
4433
4434     },
4435
4436     getAutoCreate : function()
4437     {
4438         // we will default to modal-body-overflow - might need to remove or make optional later.
4439         var bdy = {
4440                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4441                 html : this.html || ''
4442         };
4443
4444         var title = {
4445             tag: 'h5',
4446             cls : 'modal-title',
4447             html : this.title
4448         };
4449
4450         if(this.specificTitle){ // WTF is this?
4451             title = this.title;
4452         }
4453
4454         var header = [];
4455         if (this.allow_close && Roo.bootstrap.version == 3) {
4456             header.push({
4457                 tag: 'button',
4458                 cls : 'close',
4459                 html : '&times'
4460             });
4461         }
4462
4463         header.push(title);
4464
4465         if (this.editableTitle) {
4466             header.push({
4467                 cls: 'form-control roo-editable-title d-none',
4468                 tag: 'input',
4469                 type: 'text'
4470             });
4471         }
4472         
4473         if (this.allow_close && Roo.bootstrap.version == 4) {
4474             header.push({
4475                 tag: 'button',
4476                 cls : 'close',
4477                 html : '&times'
4478             });
4479         }
4480         
4481         var size = '';
4482
4483         if(this.size.length){
4484             size = 'modal-' + this.size;
4485         }
4486         
4487         var footer = Roo.bootstrap.version == 3 ?
4488             {
4489                 cls : 'modal-footer',
4490                 cn : [
4491                     {
4492                         tag: 'div',
4493                         cls: 'btn-' + this.buttonPosition
4494                     }
4495                 ]
4496
4497             } :
4498             {  // BS4 uses mr-auto on left buttons....
4499                 cls : 'modal-footer'
4500             };
4501
4502             
4503
4504         
4505         
4506         var modal = {
4507             cls: "modal",
4508              cn : [
4509                 {
4510                     cls: "modal-dialog " + size,
4511                     cn : [
4512                         {
4513                             cls : "modal-content",
4514                             cn : [
4515                                 {
4516                                     cls : 'modal-header',
4517                                     cn : header
4518                                 },
4519                                 bdy,
4520                                 footer
4521                             ]
4522
4523                         }
4524                     ]
4525
4526                 }
4527             ]
4528         };
4529
4530         if(this.animate){
4531             modal.cls += ' fade';
4532         }
4533
4534         return modal;
4535
4536     },
4537     getChildContainer : function() {
4538
4539          return this.bodyEl;
4540
4541     },
4542     getButtonContainer : function() {
4543         
4544          return Roo.bootstrap.version == 4 ?
4545             this.el.select('.modal-footer',true).first()
4546             : this.el.select('.modal-footer div',true).first();
4547
4548     },
4549     initEvents : function()
4550     {
4551         if (this.allow_close) {
4552             this.closeEl.on('click', this.hide, this);
4553         }
4554         Roo.EventManager.onWindowResize(this.resize, this, true);
4555         if (this.editableTitle) {
4556             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4557             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4558             this.headerEditEl.on('keyup', function(e) {
4559                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4560                         this.toggleHeaderInput(false)
4561                     }
4562                 }, this);
4563             this.headerEditEl.on('blur', function(e) {
4564                 this.toggleHeaderInput(false)
4565             },this);
4566         }
4567
4568     },
4569   
4570
4571     resize : function()
4572     {
4573         this.maskEl.setSize(
4574             Roo.lib.Dom.getViewWidth(true),
4575             Roo.lib.Dom.getViewHeight(true)
4576         );
4577         
4578         if (this.fitwindow) {
4579             
4580            this.dialogEl.setStyle( { 'max-width' : '100%' });
4581             this.setSize(
4582                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4583                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4584             );
4585             return;
4586         }
4587         
4588         if(this.max_width !== 0) {
4589             
4590             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4591             
4592             if(this.height) {
4593                 this.setSize(w, this.height);
4594                 return;
4595             }
4596             
4597             if(this.max_height) {
4598                 this.setSize(w,Math.min(
4599                     this.max_height,
4600                     Roo.lib.Dom.getViewportHeight(true) - 60
4601                 ));
4602                 
4603                 return;
4604             }
4605             
4606             if(!this.fit_content) {
4607                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4608                 return;
4609             }
4610             
4611             this.setSize(w, Math.min(
4612                 60 +
4613                 this.headerEl.getHeight() + 
4614                 this.footerEl.getHeight() + 
4615                 this.getChildHeight(this.bodyEl.dom.childNodes),
4616                 Roo.lib.Dom.getViewportHeight(true) - 60)
4617             );
4618         }
4619         
4620     },
4621
4622     setSize : function(w,h)
4623     {
4624         if (!w && !h) {
4625             return;
4626         }
4627         
4628         this.resizeTo(w,h);
4629         // any layout/border etc.. resize..
4630         (function () {
4631             this.items.forEach( function(e) {
4632                 e.layout ? e.layout() : false;
4633
4634             });
4635         }).defer(100,this);
4636         
4637     },
4638
4639     show : function() {
4640
4641         if (!this.rendered) {
4642             this.render();
4643         }
4644         this.toggleHeaderInput(false);
4645         //this.el.setStyle('display', 'block');
4646         this.el.removeClass('hideing');
4647         this.el.dom.style.display='block';
4648         
4649         Roo.get(document.body).addClass('modal-open');
4650  
4651         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4652             
4653             (function(){
4654                 this.el.addClass('show');
4655                 this.el.addClass('in');
4656             }).defer(50, this);
4657         }else{
4658             this.el.addClass('show');
4659             this.el.addClass('in');
4660         }
4661
4662         // not sure how we can show data in here..
4663         //if (this.tmpl) {
4664         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4665         //}
4666
4667         Roo.get(document.body).addClass("x-body-masked");
4668         
4669         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4670         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4671         this.maskEl.dom.style.display = 'block';
4672         this.maskEl.addClass('show');
4673         
4674         
4675         this.resize();
4676         
4677         this.fireEvent('show', this);
4678
4679         // set zindex here - otherwise it appears to be ignored...
4680         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4681         
4682         
4683         // this is for children that are... layout.Border 
4684         (function () {
4685             this.items.forEach( function(e) {
4686                 e.layout ? e.layout() : false;
4687
4688             });
4689         }).defer(100,this);
4690
4691     },
4692     hide : function()
4693     {
4694         if(this.fireEvent("beforehide", this) !== false){
4695             
4696             this.maskEl.removeClass('show');
4697             
4698             this.maskEl.dom.style.display = '';
4699             Roo.get(document.body).removeClass("x-body-masked");
4700             this.el.removeClass('in');
4701             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4702
4703             if(this.animate){ // why
4704                 this.el.addClass('hideing');
4705                 this.el.removeClass('show');
4706                 (function(){
4707                     if (!this.el.hasClass('hideing')) {
4708                         return; // it's been shown again...
4709                     }
4710                     
4711                     this.el.dom.style.display='';
4712
4713                     Roo.get(document.body).removeClass('modal-open');
4714                     this.el.removeClass('hideing');
4715                 }).defer(150,this);
4716                 
4717             }else{
4718                 this.el.removeClass('show');
4719                 this.el.dom.style.display='';
4720                 Roo.get(document.body).removeClass('modal-open');
4721
4722             }
4723             this.fireEvent('hide', this);
4724         }
4725     },
4726     isVisible : function()
4727     {
4728         
4729         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4730         
4731     },
4732
4733     addButton : function(str, cb)
4734     {
4735
4736
4737         var b = Roo.apply({}, { html : str } );
4738         b.xns = b.xns || Roo.bootstrap;
4739         b.xtype = b.xtype || 'Button';
4740         if (typeof(b.listeners) == 'undefined') {
4741             b.listeners = { click : cb.createDelegate(this)  };
4742         }
4743
4744         var btn = Roo.factory(b);
4745
4746         btn.render(this.getButtonContainer());
4747
4748         return btn;
4749
4750     },
4751
4752     setDefaultButton : function(btn)
4753     {
4754         //this.el.select('.modal-footer').()
4755     },
4756
4757     resizeTo: function(w,h)
4758     {
4759         this.dialogEl.setWidth(w);
4760         
4761         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4762
4763         this.bodyEl.setHeight(h - diff);
4764         
4765         this.fireEvent('resize', this);
4766     },
4767     
4768     setContentSize  : function(w, h)
4769     {
4770
4771     },
4772     onButtonClick: function(btn,e)
4773     {
4774         //Roo.log([a,b,c]);
4775         this.fireEvent('btnclick', btn.name, e);
4776     },
4777      /**
4778      * Set the title of the Dialog
4779      * @param {String} str new Title
4780      */
4781     setTitle: function(str) {
4782         this.titleEl.dom.innerHTML = str;
4783         this.title = str;
4784     },
4785     /**
4786      * Set the body of the Dialog
4787      * @param {String} str new Title
4788      */
4789     setBody: function(str) {
4790         this.bodyEl.dom.innerHTML = str;
4791     },
4792     /**
4793      * Set the body of the Dialog using the template
4794      * @param {Obj} data - apply this data to the template and replace the body contents.
4795      */
4796     applyBody: function(obj)
4797     {
4798         if (!this.tmpl) {
4799             Roo.log("Error - using apply Body without a template");
4800             //code
4801         }
4802         this.tmpl.overwrite(this.bodyEl, obj);
4803     },
4804     
4805     getChildHeight : function(child_nodes)
4806     {
4807         if(
4808             !child_nodes ||
4809             child_nodes.length == 0
4810         ) {
4811             return 0;
4812         }
4813         
4814         var child_height = 0;
4815         
4816         for(var i = 0; i < child_nodes.length; i++) {
4817             
4818             /*
4819             * for modal with tabs...
4820             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4821                 
4822                 var layout_childs = child_nodes[i].childNodes;
4823                 
4824                 for(var j = 0; j < layout_childs.length; j++) {
4825                     
4826                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4827                         
4828                         var layout_body_childs = layout_childs[j].childNodes;
4829                         
4830                         for(var k = 0; k < layout_body_childs.length; k++) {
4831                             
4832                             if(layout_body_childs[k].classList.contains('navbar')) {
4833                                 child_height += layout_body_childs[k].offsetHeight;
4834                                 continue;
4835                             }
4836                             
4837                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4838                                 
4839                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4840                                 
4841                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4842                                     
4843                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4844                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4845                                         continue;
4846                                     }
4847                                     
4848                                 }
4849                                 
4850                             }
4851                             
4852                         }
4853                     }
4854                 }
4855                 continue;
4856             }
4857             */
4858             
4859             child_height += child_nodes[i].offsetHeight;
4860             // Roo.log(child_nodes[i].offsetHeight);
4861         }
4862         
4863         return child_height;
4864     },
4865     toggleHeaderInput : function(is_edit)
4866     {
4867         if (!this.editableTitle) {
4868             return; // not editable.
4869         }
4870         if (is_edit && this.is_header_editing) {
4871             return; // already editing..
4872         }
4873         if (is_edit) {
4874     
4875             this.headerEditEl.dom.value = this.title;
4876             this.headerEditEl.removeClass('d-none');
4877             this.headerEditEl.dom.focus();
4878             this.titleEl.addClass('d-none');
4879             
4880             this.is_header_editing = true;
4881             return
4882         }
4883         // flip back to not editing.
4884         this.title = this.headerEditEl.dom.value;
4885         this.headerEditEl.addClass('d-none');
4886         this.titleEl.removeClass('d-none');
4887         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4888         this.is_header_editing = false;
4889         this.fireEvent('titlechanged', this, this.title);
4890     
4891             
4892         
4893     }
4894
4895 });
4896
4897
4898 Roo.apply(Roo.bootstrap.Modal,  {
4899     /**
4900          * Button config that displays a single OK button
4901          * @type Object
4902          */
4903         OK :  [{
4904             name : 'ok',
4905             weight : 'primary',
4906             html : 'OK'
4907         }],
4908         /**
4909          * Button config that displays Yes and No buttons
4910          * @type Object
4911          */
4912         YESNO : [
4913             {
4914                 name  : 'no',
4915                 html : 'No'
4916             },
4917             {
4918                 name  :'yes',
4919                 weight : 'primary',
4920                 html : 'Yes'
4921             }
4922         ],
4923
4924         /**
4925          * Button config that displays OK and Cancel buttons
4926          * @type Object
4927          */
4928         OKCANCEL : [
4929             {
4930                name : 'cancel',
4931                 html : 'Cancel'
4932             },
4933             {
4934                 name : 'ok',
4935                 weight : 'primary',
4936                 html : 'OK'
4937             }
4938         ],
4939         /**
4940          * Button config that displays Yes, No and Cancel buttons
4941          * @type Object
4942          */
4943         YESNOCANCEL : [
4944             {
4945                 name : 'yes',
4946                 weight : 'primary',
4947                 html : 'Yes'
4948             },
4949             {
4950                 name : 'no',
4951                 html : 'No'
4952             },
4953             {
4954                 name : 'cancel',
4955                 html : 'Cancel'
4956             }
4957         ],
4958         
4959         zIndex : 10001
4960 });
4961
4962 /*
4963  * - LGPL
4964  *
4965  * messagebox - can be used as a replace
4966  * 
4967  */
4968 /**
4969  * @class Roo.MessageBox
4970  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4971  * Example usage:
4972  *<pre><code>
4973 // Basic alert:
4974 Roo.Msg.alert('Status', 'Changes saved successfully.');
4975
4976 // Prompt for user data:
4977 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4978     if (btn == 'ok'){
4979         // process text value...
4980     }
4981 });
4982
4983 // Show a dialog using config options:
4984 Roo.Msg.show({
4985    title:'Save Changes?',
4986    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4987    buttons: Roo.Msg.YESNOCANCEL,
4988    fn: processResult,
4989    animEl: 'elId'
4990 });
4991 </code></pre>
4992  * @static
4993  */
4994 Roo.bootstrap.MessageBox = function(){
4995     var dlg, opt, mask, waitTimer;
4996     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4997     var buttons, activeTextEl, bwidth;
4998
4999     
5000     // private
5001     var handleButton = function(button){
5002         dlg.hide();
5003         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5004     };
5005
5006     // private
5007     var handleHide = function(){
5008         if(opt && opt.cls){
5009             dlg.el.removeClass(opt.cls);
5010         }
5011         //if(waitTimer){
5012         //    Roo.TaskMgr.stop(waitTimer);
5013         //    waitTimer = null;
5014         //}
5015     };
5016
5017     // private
5018     var updateButtons = function(b){
5019         var width = 0;
5020         if(!b){
5021             buttons["ok"].hide();
5022             buttons["cancel"].hide();
5023             buttons["yes"].hide();
5024             buttons["no"].hide();
5025             dlg.footerEl.hide();
5026             
5027             return width;
5028         }
5029         dlg.footerEl.show();
5030         for(var k in buttons){
5031             if(typeof buttons[k] != "function"){
5032                 if(b[k]){
5033                     buttons[k].show();
5034                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5035                     width += buttons[k].el.getWidth()+15;
5036                 }else{
5037                     buttons[k].hide();
5038                 }
5039             }
5040         }
5041         return width;
5042     };
5043
5044     // private
5045     var handleEsc = function(d, k, e){
5046         if(opt && opt.closable !== false){
5047             dlg.hide();
5048         }
5049         if(e){
5050             e.stopEvent();
5051         }
5052     };
5053
5054     return {
5055         /**
5056          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5057          * @return {Roo.BasicDialog} The BasicDialog element
5058          */
5059         getDialog : function(){
5060            if(!dlg){
5061                 dlg = new Roo.bootstrap.Modal( {
5062                     //draggable: true,
5063                     //resizable:false,
5064                     //constraintoviewport:false,
5065                     //fixedcenter:true,
5066                     //collapsible : false,
5067                     //shim:true,
5068                     //modal: true,
5069                 //    width: 'auto',
5070                   //  height:100,
5071                     //buttonAlign:"center",
5072                     closeClick : function(){
5073                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5074                             handleButton("no");
5075                         }else{
5076                             handleButton("cancel");
5077                         }
5078                     }
5079                 });
5080                 dlg.render();
5081                 dlg.on("hide", handleHide);
5082                 mask = dlg.mask;
5083                 //dlg.addKeyListener(27, handleEsc);
5084                 buttons = {};
5085                 this.buttons = buttons;
5086                 var bt = this.buttonText;
5087                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5088                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5089                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5090                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5091                 //Roo.log(buttons);
5092                 bodyEl = dlg.bodyEl.createChild({
5093
5094                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5095                         '<textarea class="roo-mb-textarea"></textarea>' +
5096                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5097                 });
5098                 msgEl = bodyEl.dom.firstChild;
5099                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5100                 textboxEl.enableDisplayMode();
5101                 textboxEl.addKeyListener([10,13], function(){
5102                     if(dlg.isVisible() && opt && opt.buttons){
5103                         if(opt.buttons.ok){
5104                             handleButton("ok");
5105                         }else if(opt.buttons.yes){
5106                             handleButton("yes");
5107                         }
5108                     }
5109                 });
5110                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5111                 textareaEl.enableDisplayMode();
5112                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5113                 progressEl.enableDisplayMode();
5114                 
5115                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5116                 var pf = progressEl.dom.firstChild;
5117                 if (pf) {
5118                     pp = Roo.get(pf.firstChild);
5119                     pp.setHeight(pf.offsetHeight);
5120                 }
5121                 
5122             }
5123             return dlg;
5124         },
5125
5126         /**
5127          * Updates the message box body text
5128          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5129          * the XHTML-compliant non-breaking space character '&amp;#160;')
5130          * @return {Roo.MessageBox} This message box
5131          */
5132         updateText : function(text)
5133         {
5134             if(!dlg.isVisible() && !opt.width){
5135                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5136                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5137             }
5138             msgEl.innerHTML = text || '&#160;';
5139       
5140             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5141             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5142             var w = Math.max(
5143                     Math.min(opt.width || cw , this.maxWidth), 
5144                     Math.max(opt.minWidth || this.minWidth, bwidth)
5145             );
5146             if(opt.prompt){
5147                 activeTextEl.setWidth(w);
5148             }
5149             if(dlg.isVisible()){
5150                 dlg.fixedcenter = false;
5151             }
5152             // to big, make it scroll. = But as usual stupid IE does not support
5153             // !important..
5154             
5155             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5156                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5157                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5158             } else {
5159                 bodyEl.dom.style.height = '';
5160                 bodyEl.dom.style.overflowY = '';
5161             }
5162             if (cw > w) {
5163                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5164             } else {
5165                 bodyEl.dom.style.overflowX = '';
5166             }
5167             
5168             dlg.setContentSize(w, bodyEl.getHeight());
5169             if(dlg.isVisible()){
5170                 dlg.fixedcenter = true;
5171             }
5172             return this;
5173         },
5174
5175         /**
5176          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5177          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5178          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5179          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5180          * @return {Roo.MessageBox} This message box
5181          */
5182         updateProgress : function(value, text){
5183             if(text){
5184                 this.updateText(text);
5185             }
5186             
5187             if (pp) { // weird bug on my firefox - for some reason this is not defined
5188                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5189                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5190             }
5191             return this;
5192         },        
5193
5194         /**
5195          * Returns true if the message box is currently displayed
5196          * @return {Boolean} True if the message box is visible, else false
5197          */
5198         isVisible : function(){
5199             return dlg && dlg.isVisible();  
5200         },
5201
5202         /**
5203          * Hides the message box if it is displayed
5204          */
5205         hide : function(){
5206             if(this.isVisible()){
5207                 dlg.hide();
5208             }  
5209         },
5210
5211         /**
5212          * Displays a new message box, or reinitializes an existing message box, based on the config options
5213          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5214          * The following config object properties are supported:
5215          * <pre>
5216 Property    Type             Description
5217 ----------  ---------------  ------------------------------------------------------------------------------------
5218 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5219                                    closes (defaults to undefined)
5220 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5221                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5222 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5223                                    progress and wait dialogs will ignore this property and always hide the
5224                                    close button as they can only be closed programmatically.
5225 cls               String           A custom CSS class to apply to the message box element
5226 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5227                                    displayed (defaults to 75)
5228 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5229                                    function will be btn (the name of the button that was clicked, if applicable,
5230                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5231                                    Progress and wait dialogs will ignore this option since they do not respond to
5232                                    user actions and can only be closed programmatically, so any required function
5233                                    should be called by the same code after it closes the dialog.
5234 icon              String           A CSS class that provides a background image to be used as an icon for
5235                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5236 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5237 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5238 modal             Boolean          False to allow user interaction with the page while the message box is
5239                                    displayed (defaults to true)
5240 msg               String           A string that will replace the existing message box body text (defaults
5241                                    to the XHTML-compliant non-breaking space character '&#160;')
5242 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5243 progress          Boolean          True to display a progress bar (defaults to false)
5244 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5245 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5246 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5247 title             String           The title text
5248 value             String           The string value to set into the active textbox element if displayed
5249 wait              Boolean          True to display a progress bar (defaults to false)
5250 width             Number           The width of the dialog in pixels
5251 </pre>
5252          *
5253          * Example usage:
5254          * <pre><code>
5255 Roo.Msg.show({
5256    title: 'Address',
5257    msg: 'Please enter your address:',
5258    width: 300,
5259    buttons: Roo.MessageBox.OKCANCEL,
5260    multiline: true,
5261    fn: saveAddress,
5262    animEl: 'addAddressBtn'
5263 });
5264 </code></pre>
5265          * @param {Object} config Configuration options
5266          * @return {Roo.MessageBox} This message box
5267          */
5268         show : function(options)
5269         {
5270             
5271             // this causes nightmares if you show one dialog after another
5272             // especially on callbacks..
5273              
5274             if(this.isVisible()){
5275                 
5276                 this.hide();
5277                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5278                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5279                 Roo.log("New Dialog Message:" +  options.msg )
5280                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5281                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5282                 
5283             }
5284             var d = this.getDialog();
5285             opt = options;
5286             d.setTitle(opt.title || "&#160;");
5287             d.closeEl.setDisplayed(opt.closable !== false);
5288             activeTextEl = textboxEl;
5289             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5290             if(opt.prompt){
5291                 if(opt.multiline){
5292                     textboxEl.hide();
5293                     textareaEl.show();
5294                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5295                         opt.multiline : this.defaultTextHeight);
5296                     activeTextEl = textareaEl;
5297                 }else{
5298                     textboxEl.show();
5299                     textareaEl.hide();
5300                 }
5301             }else{
5302                 textboxEl.hide();
5303                 textareaEl.hide();
5304             }
5305             progressEl.setDisplayed(opt.progress === true);
5306             if (opt.progress) {
5307                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5308             }
5309             this.updateProgress(0);
5310             activeTextEl.dom.value = opt.value || "";
5311             if(opt.prompt){
5312                 dlg.setDefaultButton(activeTextEl);
5313             }else{
5314                 var bs = opt.buttons;
5315                 var db = null;
5316                 if(bs && bs.ok){
5317                     db = buttons["ok"];
5318                 }else if(bs && bs.yes){
5319                     db = buttons["yes"];
5320                 }
5321                 dlg.setDefaultButton(db);
5322             }
5323             bwidth = updateButtons(opt.buttons);
5324             this.updateText(opt.msg);
5325             if(opt.cls){
5326                 d.el.addClass(opt.cls);
5327             }
5328             d.proxyDrag = opt.proxyDrag === true;
5329             d.modal = opt.modal !== false;
5330             d.mask = opt.modal !== false ? mask : false;
5331             if(!d.isVisible()){
5332                 // force it to the end of the z-index stack so it gets a cursor in FF
5333                 document.body.appendChild(dlg.el.dom);
5334                 d.animateTarget = null;
5335                 d.show(options.animEl);
5336             }
5337             return this;
5338         },
5339
5340         /**
5341          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5342          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5343          * and closing the message box when the process is complete.
5344          * @param {String} title The title bar text
5345          * @param {String} msg The message box body text
5346          * @return {Roo.MessageBox} This message box
5347          */
5348         progress : function(title, msg){
5349             this.show({
5350                 title : title,
5351                 msg : msg,
5352                 buttons: false,
5353                 progress:true,
5354                 closable:false,
5355                 minWidth: this.minProgressWidth,
5356                 modal : true
5357             });
5358             return this;
5359         },
5360
5361         /**
5362          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5363          * If a callback function is passed it will be called after the user clicks the button, and the
5364          * id of the button that was clicked will be passed as the only parameter to the callback
5365          * (could also be the top-right close button).
5366          * @param {String} title The title bar text
5367          * @param {String} msg The message box body text
5368          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5369          * @param {Object} scope (optional) The scope of the callback function
5370          * @return {Roo.MessageBox} This message box
5371          */
5372         alert : function(title, msg, fn, scope)
5373         {
5374             this.show({
5375                 title : title,
5376                 msg : msg,
5377                 buttons: this.OK,
5378                 fn: fn,
5379                 closable : false,
5380                 scope : scope,
5381                 modal : true
5382             });
5383             return this;
5384         },
5385
5386         /**
5387          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5388          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5389          * You are responsible for closing the message box when the process is complete.
5390          * @param {String} msg The message box body text
5391          * @param {String} title (optional) The title bar text
5392          * @return {Roo.MessageBox} This message box
5393          */
5394         wait : function(msg, title){
5395             this.show({
5396                 title : title,
5397                 msg : msg,
5398                 buttons: false,
5399                 closable:false,
5400                 progress:true,
5401                 modal:true,
5402                 width:300,
5403                 wait:true
5404             });
5405             waitTimer = Roo.TaskMgr.start({
5406                 run: function(i){
5407                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5408                 },
5409                 interval: 1000
5410             });
5411             return this;
5412         },
5413
5414         /**
5415          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5416          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5417          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5418          * @param {String} title The title bar text
5419          * @param {String} msg The message box body text
5420          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5421          * @param {Object} scope (optional) The scope of the callback function
5422          * @return {Roo.MessageBox} This message box
5423          */
5424         confirm : function(title, msg, fn, scope){
5425             this.show({
5426                 title : title,
5427                 msg : msg,
5428                 buttons: this.YESNO,
5429                 fn: fn,
5430                 scope : scope,
5431                 modal : true
5432             });
5433             return this;
5434         },
5435
5436         /**
5437          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5438          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5439          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5440          * (could also be the top-right close button) and the text that was entered will be passed as the two
5441          * parameters to the callback.
5442          * @param {String} title The title bar text
5443          * @param {String} msg The message box body text
5444          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5445          * @param {Object} scope (optional) The scope of the callback function
5446          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5447          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5448          * @return {Roo.MessageBox} This message box
5449          */
5450         prompt : function(title, msg, fn, scope, multiline){
5451             this.show({
5452                 title : title,
5453                 msg : msg,
5454                 buttons: this.OKCANCEL,
5455                 fn: fn,
5456                 minWidth:250,
5457                 scope : scope,
5458                 prompt:true,
5459                 multiline: multiline,
5460                 modal : true
5461             });
5462             return this;
5463         },
5464
5465         /**
5466          * Button config that displays a single OK button
5467          * @type Object
5468          */
5469         OK : {ok:true},
5470         /**
5471          * Button config that displays Yes and No buttons
5472          * @type Object
5473          */
5474         YESNO : {yes:true, no:true},
5475         /**
5476          * Button config that displays OK and Cancel buttons
5477          * @type Object
5478          */
5479         OKCANCEL : {ok:true, cancel:true},
5480         /**
5481          * Button config that displays Yes, No and Cancel buttons
5482          * @type Object
5483          */
5484         YESNOCANCEL : {yes:true, no:true, cancel:true},
5485
5486         /**
5487          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5488          * @type Number
5489          */
5490         defaultTextHeight : 75,
5491         /**
5492          * The maximum width in pixels of the message box (defaults to 600)
5493          * @type Number
5494          */
5495         maxWidth : 600,
5496         /**
5497          * The minimum width in pixels of the message box (defaults to 100)
5498          * @type Number
5499          */
5500         minWidth : 100,
5501         /**
5502          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5503          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5504          * @type Number
5505          */
5506         minProgressWidth : 250,
5507         /**
5508          * An object containing the default button text strings that can be overriden for localized language support.
5509          * Supported properties are: ok, cancel, yes and no.
5510          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5511          * @type Object
5512          */
5513         buttonText : {
5514             ok : "OK",
5515             cancel : "Cancel",
5516             yes : "Yes",
5517             no : "No"
5518         }
5519     };
5520 }();
5521
5522 /**
5523  * Shorthand for {@link Roo.MessageBox}
5524  */
5525 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5526 Roo.Msg = Roo.Msg || Roo.MessageBox;
5527 /*
5528  * - LGPL
5529  *
5530  * navbar
5531  * 
5532  */
5533
5534 /**
5535  * @class Roo.bootstrap.nav.Bar
5536  * @extends Roo.bootstrap.Component
5537  * @abstract
5538  * Bootstrap Navbar class
5539
5540  * @constructor
5541  * Create a new Navbar
5542  * @param {Object} config The config object
5543  */
5544
5545
5546 Roo.bootstrap.nav.Bar = function(config){
5547     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5548     this.addEvents({
5549         // raw events
5550         /**
5551          * @event beforetoggle
5552          * Fire before toggle the menu
5553          * @param {Roo.EventObject} e
5554          */
5555         "beforetoggle" : true
5556     });
5557 };
5558
5559 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5560     
5561     
5562    
5563     // private
5564     navItems : false,
5565     loadMask : false,
5566     
5567     
5568     getAutoCreate : function(){
5569         
5570         
5571         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5572         
5573     },
5574     
5575     initEvents :function ()
5576     {
5577         //Roo.log(this.el.select('.navbar-toggle',true));
5578         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5579         
5580         var mark = {
5581             tag: "div",
5582             cls:"x-dlg-mask"
5583         };
5584         
5585         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5586         
5587         var size = this.el.getSize();
5588         this.maskEl.setSize(size.width, size.height);
5589         this.maskEl.enableDisplayMode("block");
5590         this.maskEl.hide();
5591         
5592         if(this.loadMask){
5593             this.maskEl.show();
5594         }
5595     },
5596     
5597     
5598     getChildContainer : function()
5599     {
5600         if (this.el && this.el.select('.collapse').getCount()) {
5601             return this.el.select('.collapse',true).first();
5602         }
5603         
5604         return this.el;
5605     },
5606     
5607     mask : function()
5608     {
5609         this.maskEl.show();
5610     },
5611     
5612     unmask : function()
5613     {
5614         this.maskEl.hide();
5615     },
5616     onToggle : function()
5617     {
5618         
5619         if(this.fireEvent('beforetoggle', this) === false){
5620             return;
5621         }
5622         var ce = this.el.select('.navbar-collapse',true).first();
5623       
5624         if (!ce.hasClass('show')) {
5625            this.expand();
5626         } else {
5627             this.collapse();
5628         }
5629         
5630         
5631     
5632     },
5633     /**
5634      * Expand the navbar pulldown 
5635      */
5636     expand : function ()
5637     {
5638        
5639         var ce = this.el.select('.navbar-collapse',true).first();
5640         if (ce.hasClass('collapsing')) {
5641             return;
5642         }
5643         ce.dom.style.height = '';
5644                // show it...
5645         ce.addClass('in'); // old...
5646         ce.removeClass('collapse');
5647         ce.addClass('show');
5648         var h = ce.getHeight();
5649         Roo.log(h);
5650         ce.removeClass('show');
5651         // at this point we should be able to see it..
5652         ce.addClass('collapsing');
5653         
5654         ce.setHeight(0); // resize it ...
5655         ce.on('transitionend', function() {
5656             //Roo.log('done transition');
5657             ce.removeClass('collapsing');
5658             ce.addClass('show');
5659             ce.removeClass('collapse');
5660
5661             ce.dom.style.height = '';
5662         }, this, { single: true} );
5663         ce.setHeight(h);
5664         ce.dom.scrollTop = 0;
5665     },
5666     /**
5667      * Collapse the navbar pulldown 
5668      */
5669     collapse : function()
5670     {
5671          var ce = this.el.select('.navbar-collapse',true).first();
5672        
5673         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5674             // it's collapsed or collapsing..
5675             return;
5676         }
5677         ce.removeClass('in'); // old...
5678         ce.setHeight(ce.getHeight());
5679         ce.removeClass('show');
5680         ce.addClass('collapsing');
5681         
5682         ce.on('transitionend', function() {
5683             ce.dom.style.height = '';
5684             ce.removeClass('collapsing');
5685             ce.addClass('collapse');
5686         }, this, { single: true} );
5687         ce.setHeight(0);
5688     }
5689     
5690     
5691     
5692 });
5693
5694
5695
5696  
5697
5698  /*
5699  * - LGPL
5700  *
5701  * navbar
5702  * 
5703  */
5704
5705 /**
5706  * @class Roo.bootstrap.nav.Simplebar
5707  * @extends Roo.bootstrap.nav.Bar
5708  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5709  * Bootstrap Sidebar class
5710  *
5711  * @cfg {Boolean} inverse is inverted color
5712  * 
5713  * @cfg {String} type (nav | pills | tabs)
5714  * @cfg {Boolean} arrangement stacked | justified
5715  * @cfg {String} align (left | right) alignment
5716  * 
5717  * @cfg {Boolean} main (true|false) main nav bar? default false
5718  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5719  * 
5720  * @cfg {String} tag (header|footer|nav|div) default is nav 
5721
5722  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5723  * 
5724  * 
5725  * @constructor
5726  * Create a new Sidebar
5727  * @param {Object} config The config object
5728  */
5729
5730
5731 Roo.bootstrap.nav.Simplebar = function(config){
5732     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5733 };
5734
5735 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5736     
5737     inverse: false,
5738     
5739     type: false,
5740     arrangement: '',
5741     align : false,
5742     
5743     weight : 'light',
5744     
5745     main : false,
5746     
5747     
5748     tag : false,
5749     
5750     
5751     getAutoCreate : function(){
5752         
5753         
5754         var cfg = {
5755             tag : this.tag || 'div',
5756             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5757         };
5758         if (['light','white'].indexOf(this.weight) > -1) {
5759             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5760         }
5761         cfg.cls += ' bg-' + this.weight;
5762         
5763         if (this.inverse) {
5764             cfg.cls += ' navbar-inverse';
5765             
5766         }
5767         
5768         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5769         
5770         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5771             return cfg;
5772         }
5773         
5774         
5775     
5776         
5777         cfg.cn = [
5778             {
5779                 cls: 'nav nav-' + this.xtype,
5780                 tag : 'ul'
5781             }
5782         ];
5783         
5784          
5785         this.type = this.type || 'nav';
5786         if (['tabs','pills'].indexOf(this.type) != -1) {
5787             cfg.cn[0].cls += ' nav-' + this.type
5788         
5789         
5790         } else {
5791             if (this.type!=='nav') {
5792                 Roo.log('nav type must be nav/tabs/pills')
5793             }
5794             cfg.cn[0].cls += ' navbar-nav'
5795         }
5796         
5797         
5798         
5799         
5800         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5801             cfg.cn[0].cls += ' nav-' + this.arrangement;
5802         }
5803         
5804         
5805         if (this.align === 'right') {
5806             cfg.cn[0].cls += ' navbar-right';
5807         }
5808         
5809         
5810         
5811         
5812         return cfg;
5813     
5814         
5815     }
5816     
5817     
5818     
5819 });
5820
5821
5822
5823  
5824
5825  
5826        /*
5827  * - LGPL
5828  *
5829  * navbar
5830  * navbar-fixed-top
5831  * navbar-expand-md  fixed-top 
5832  */
5833
5834 /**
5835  * @class Roo.bootstrap.nav.Headerbar
5836  * @extends Roo.bootstrap.nav.Simplebar
5837  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5838  * Bootstrap Sidebar class
5839  *
5840  * @cfg {String} brand what is brand
5841  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5842  * @cfg {String} brand_href href of the brand
5843  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5844  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5845  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5846  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5847  * 
5848  * @constructor
5849  * Create a new Sidebar
5850  * @param {Object} config The config object
5851  */
5852
5853
5854 Roo.bootstrap.nav.Headerbar = function(config){
5855     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5856       
5857 };
5858
5859 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5860     
5861     position: '',
5862     brand: '',
5863     brand_href: false,
5864     srButton : true,
5865     autohide : false,
5866     desktopCenter : false,
5867    
5868     
5869     getAutoCreate : function(){
5870         
5871         var   cfg = {
5872             tag: this.nav || 'nav',
5873             cls: 'navbar navbar-expand-md',
5874             role: 'navigation',
5875             cn: []
5876         };
5877         
5878         var cn = cfg.cn;
5879         if (this.desktopCenter) {
5880             cn.push({cls : 'container', cn : []});
5881             cn = cn[0].cn;
5882         }
5883         
5884         if(this.srButton){
5885             var btn = {
5886                 tag: 'button',
5887                 type: 'button',
5888                 cls: 'navbar-toggle navbar-toggler',
5889                 'data-toggle': 'collapse',
5890                 cn: [
5891                     {
5892                         tag: 'span',
5893                         cls: 'sr-only',
5894                         html: 'Toggle navigation'
5895                     },
5896                     {
5897                         tag: 'span',
5898                         cls: 'icon-bar navbar-toggler-icon'
5899                     },
5900                     {
5901                         tag: 'span',
5902                         cls: 'icon-bar'
5903                     },
5904                     {
5905                         tag: 'span',
5906                         cls: 'icon-bar'
5907                     }
5908                 ]
5909             };
5910             
5911             cn.push( Roo.bootstrap.version == 4 ? btn : {
5912                 tag: 'div',
5913                 cls: 'navbar-header',
5914                 cn: [
5915                     btn
5916                 ]
5917             });
5918         }
5919         
5920         cn.push({
5921             tag: 'div',
5922             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5923             cn : []
5924         });
5925         
5926         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5927         
5928         if (['light','white'].indexOf(this.weight) > -1) {
5929             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5930         }
5931         cfg.cls += ' bg-' + this.weight;
5932         
5933         
5934         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5935             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5936             
5937             // tag can override this..
5938             
5939             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5940         }
5941         
5942         if (this.brand !== '') {
5943             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5944             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5945                 tag: 'a',
5946                 href: this.brand_href ? this.brand_href : '#',
5947                 cls: 'navbar-brand',
5948                 cn: [
5949                 this.brand
5950                 ]
5951             });
5952         }
5953         
5954         if(this.main){
5955             cfg.cls += ' main-nav';
5956         }
5957         
5958         
5959         return cfg;
5960
5961         
5962     },
5963     getHeaderChildContainer : function()
5964     {
5965         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5966             return this.el.select('.navbar-header',true).first();
5967         }
5968         
5969         return this.getChildContainer();
5970     },
5971     
5972     getChildContainer : function()
5973     {
5974          
5975         return this.el.select('.roo-navbar-collapse',true).first();
5976          
5977         
5978     },
5979     
5980     initEvents : function()
5981     {
5982         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5983         
5984         if (this.autohide) {
5985             
5986             var prevScroll = 0;
5987             var ft = this.el;
5988             
5989             Roo.get(document).on('scroll',function(e) {
5990                 var ns = Roo.get(document).getScroll().top;
5991                 var os = prevScroll;
5992                 prevScroll = ns;
5993                 
5994                 if(ns > os){
5995                     ft.removeClass('slideDown');
5996                     ft.addClass('slideUp');
5997                     return;
5998                 }
5999                 ft.removeClass('slideUp');
6000                 ft.addClass('slideDown');
6001                  
6002               
6003           },this);
6004         }
6005     }    
6006     
6007 });
6008
6009
6010
6011  
6012
6013  /*
6014  * - LGPL
6015  *
6016  * navbar
6017  * 
6018  */
6019
6020 /**
6021  * @class Roo.bootstrap.nav.Sidebar
6022  * @extends Roo.bootstrap.nav.Bar
6023  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6024  * Bootstrap Sidebar class
6025  * 
6026  * @constructor
6027  * Create a new Sidebar
6028  * @param {Object} config The config object
6029  */
6030
6031
6032 Roo.bootstrap.nav.Sidebar = function(config){
6033     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6034 };
6035
6036 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6037     
6038     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6039     
6040     getAutoCreate : function(){
6041         
6042         
6043         return  {
6044             tag: 'div',
6045             cls: 'sidebar sidebar-nav'
6046         };
6047     
6048         
6049     }
6050     
6051     
6052     
6053 });
6054
6055
6056
6057  
6058
6059  /*
6060  * - LGPL
6061  *
6062  * nav group
6063  * 
6064  */
6065
6066 /**
6067  * @class Roo.bootstrap.nav.Group
6068  * @extends Roo.bootstrap.Component
6069  * @children Roo.bootstrap.nav.Item
6070  * Bootstrap NavGroup class
6071  * @cfg {String} align (left|right)
6072  * @cfg {Boolean} inverse
6073  * @cfg {String} type (nav|pills|tab) default nav
6074  * @cfg {String} navId - reference Id for navbar.
6075  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6076  * 
6077  * @constructor
6078  * Create a new nav group
6079  * @param {Object} config The config object
6080  */
6081
6082 Roo.bootstrap.nav.Group = function(config){
6083     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6084     this.navItems = [];
6085    
6086     Roo.bootstrap.nav.Group.register(this);
6087      this.addEvents({
6088         /**
6089              * @event changed
6090              * Fires when the active item changes
6091              * @param {Roo.bootstrap.nav.Group} this
6092              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6093              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6094          */
6095         'changed': true
6096      });
6097     
6098 };
6099
6100 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6101     
6102     align: '',
6103     inverse: false,
6104     form: false,
6105     type: 'nav',
6106     navId : '',
6107     // private
6108     pilltype : true,
6109     
6110     navItems : false, 
6111     
6112     getAutoCreate : function()
6113     {
6114         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6115         
6116         cfg = {
6117             tag : 'ul',
6118             cls: 'nav' 
6119         };
6120         if (Roo.bootstrap.version == 4) {
6121             if (['tabs','pills'].indexOf(this.type) != -1) {
6122                 cfg.cls += ' nav-' + this.type; 
6123             } else {
6124                 // trying to remove so header bar can right align top?
6125                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6126                     // do not use on header bar... 
6127                     cfg.cls += ' navbar-nav';
6128                 }
6129             }
6130             
6131         } else {
6132             if (['tabs','pills'].indexOf(this.type) != -1) {
6133                 cfg.cls += ' nav-' + this.type
6134             } else {
6135                 if (this.type !== 'nav') {
6136                     Roo.log('nav type must be nav/tabs/pills')
6137                 }
6138                 cfg.cls += ' navbar-nav'
6139             }
6140         }
6141         
6142         if (this.parent() && this.parent().sidebar) {
6143             cfg = {
6144                 tag: 'ul',
6145                 cls: 'dashboard-menu sidebar-menu'
6146             };
6147             
6148             return cfg;
6149         }
6150         
6151         if (this.form === true) {
6152             cfg = {
6153                 tag: 'form',
6154                 cls: 'navbar-form form-inline'
6155             };
6156             //nav navbar-right ml-md-auto
6157             if (this.align === 'right') {
6158                 cfg.cls += ' navbar-right ml-md-auto';
6159             } else {
6160                 cfg.cls += ' navbar-left';
6161             }
6162         }
6163         
6164         if (this.align === 'right') {
6165             cfg.cls += ' navbar-right ml-md-auto';
6166         } else {
6167             cfg.cls += ' mr-auto';
6168         }
6169         
6170         if (this.inverse) {
6171             cfg.cls += ' navbar-inverse';
6172             
6173         }
6174         
6175         
6176         return cfg;
6177     },
6178     /**
6179     * sets the active Navigation item
6180     * @param {Roo.bootstrap.nav.Item} the new current navitem
6181     */
6182     setActiveItem : function(item)
6183     {
6184         var prev = false;
6185         Roo.each(this.navItems, function(v){
6186             if (v == item) {
6187                 return ;
6188             }
6189             if (v.isActive()) {
6190                 v.setActive(false, true);
6191                 prev = v;
6192                 
6193             }
6194             
6195         });
6196
6197         item.setActive(true, true);
6198         this.fireEvent('changed', this, item, prev);
6199         
6200         
6201     },
6202     /**
6203     * gets the active Navigation item
6204     * @return {Roo.bootstrap.nav.Item} the current navitem
6205     */
6206     getActive : function()
6207     {
6208         
6209         var prev = false;
6210         Roo.each(this.navItems, function(v){
6211             
6212             if (v.isActive()) {
6213                 prev = v;
6214                 
6215             }
6216             
6217         });
6218         return prev;
6219     },
6220     
6221     indexOfNav : function()
6222     {
6223         
6224         var prev = false;
6225         Roo.each(this.navItems, function(v,i){
6226             
6227             if (v.isActive()) {
6228                 prev = i;
6229                 
6230             }
6231             
6232         });
6233         return prev;
6234     },
6235     /**
6236     * adds a Navigation item
6237     * @param {Roo.bootstrap.nav.Item} the navitem to add
6238     */
6239     addItem : function(cfg)
6240     {
6241         if (this.form && Roo.bootstrap.version == 4) {
6242             cfg.tag = 'div';
6243         }
6244         var cn = new Roo.bootstrap.nav.Item(cfg);
6245         this.register(cn);
6246         cn.parentId = this.id;
6247         cn.onRender(this.el, null);
6248         return cn;
6249     },
6250     /**
6251     * register a Navigation item
6252     * @param {Roo.bootstrap.nav.Item} the navitem to add
6253     */
6254     register : function(item)
6255     {
6256         this.navItems.push( item);
6257         item.navId = this.navId;
6258     
6259     },
6260     
6261     /**
6262     * clear all the Navigation item
6263     */
6264    
6265     clearAll : function()
6266     {
6267         this.navItems = [];
6268         this.el.dom.innerHTML = '';
6269     },
6270     
6271     getNavItem: function(tabId)
6272     {
6273         var ret = false;
6274         Roo.each(this.navItems, function(e) {
6275             if (e.tabId == tabId) {
6276                ret =  e;
6277                return false;
6278             }
6279             return true;
6280             
6281         });
6282         return ret;
6283     },
6284     
6285     setActiveNext : function()
6286     {
6287         var i = this.indexOfNav(this.getActive());
6288         if (i > this.navItems.length) {
6289             return;
6290         }
6291         this.setActiveItem(this.navItems[i+1]);
6292     },
6293     setActivePrev : function()
6294     {
6295         var i = this.indexOfNav(this.getActive());
6296         if (i  < 1) {
6297             return;
6298         }
6299         this.setActiveItem(this.navItems[i-1]);
6300     },
6301     clearWasActive : function(except) {
6302         Roo.each(this.navItems, function(e) {
6303             if (e.tabId != except.tabId && e.was_active) {
6304                e.was_active = false;
6305                return false;
6306             }
6307             return true;
6308             
6309         });
6310     },
6311     getWasActive : function ()
6312     {
6313         var r = false;
6314         Roo.each(this.navItems, function(e) {
6315             if (e.was_active) {
6316                r = e;
6317                return false;
6318             }
6319             return true;
6320             
6321         });
6322         return r;
6323     }
6324     
6325     
6326 });
6327
6328  
6329 Roo.apply(Roo.bootstrap.nav.Group, {
6330     
6331     groups: {},
6332      /**
6333     * register a Navigation Group
6334     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6335     */
6336     register : function(navgrp)
6337     {
6338         this.groups[navgrp.navId] = navgrp;
6339         
6340     },
6341     /**
6342     * fetch a Navigation Group based on the navigation ID
6343     * @param {string} the navgroup to add
6344     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6345     */
6346     get: function(navId) {
6347         if (typeof(this.groups[navId]) == 'undefined') {
6348             return false;
6349             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6350         }
6351         return this.groups[navId] ;
6352     }
6353     
6354     
6355     
6356 });
6357
6358  /**
6359  * @class Roo.bootstrap.nav.Item
6360  * @extends Roo.bootstrap.Component
6361  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6362  * @parent Roo.bootstrap.nav.Group
6363  * @licence LGPL
6364  * Bootstrap Navbar.NavItem class
6365  * 
6366  * @cfg {String} href  link to
6367  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6368  * @cfg {Boolean} button_outline show and outlined button
6369  * @cfg {String} html content of button
6370  * @cfg {String} badge text inside badge
6371  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6372  * @cfg {String} glyphicon DEPRICATED - use fa
6373  * @cfg {String} icon DEPRICATED - use fa
6374  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6375  * @cfg {Boolean} active Is item active
6376  * @cfg {Boolean} disabled Is item disabled
6377  * @cfg {String} linkcls  Link Class
6378  * @cfg {Boolean} preventDefault (true | false) default false
6379  * @cfg {String} tabId the tab that this item activates.
6380  * @cfg {String} tagtype (a|span) render as a href or span?
6381  * @cfg {Boolean} animateRef (true|false) link to element default false  
6382  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6383   
6384  * @constructor
6385  * Create a new Navbar Item
6386  * @param {Object} config The config object
6387  */
6388 Roo.bootstrap.nav.Item = function(config){
6389     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6390     this.addEvents({
6391         // raw events
6392         /**
6393          * @event click
6394          * The raw click event for the entire grid.
6395          * @param {Roo.EventObject} e
6396          */
6397         "click" : true,
6398          /**
6399             * @event changed
6400             * Fires when the active item active state changes
6401             * @param {Roo.bootstrap.nav.Item} this
6402             * @param {boolean} state the new state
6403              
6404          */
6405         'changed': true,
6406         /**
6407             * @event scrollto
6408             * Fires when scroll to element
6409             * @param {Roo.bootstrap.nav.Item} this
6410             * @param {Object} options
6411             * @param {Roo.EventObject} e
6412              
6413          */
6414         'scrollto': true
6415     });
6416    
6417 };
6418
6419 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6420     
6421     href: false,
6422     html: '',
6423     badge: '',
6424     icon: false,
6425     fa : false,
6426     glyphicon: false,
6427     active: false,
6428     preventDefault : false,
6429     tabId : false,
6430     tagtype : 'a',
6431     tag: 'li',
6432     disabled : false,
6433     animateRef : false,
6434     was_active : false,
6435     button_weight : '',
6436     button_outline : false,
6437     linkcls : '',
6438     navLink: false,
6439     
6440     getAutoCreate : function(){
6441          
6442         var cfg = {
6443             tag: this.tag,
6444             cls: 'nav-item'
6445         };
6446         
6447         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6448         
6449         if (this.active) {
6450             cfg.cls +=  ' active' ;
6451         }
6452         if (this.disabled) {
6453             cfg.cls += ' disabled';
6454         }
6455         
6456         // BS4 only?
6457         if (this.button_weight.length) {
6458             cfg.tag = this.href ? 'a' : 'button';
6459             cfg.html = this.html || '';
6460             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6461             if (this.href) {
6462                 cfg.href = this.href;
6463             }
6464             if (this.fa) {
6465                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6466             } else {
6467                 cfg.cls += " nav-html";
6468             }
6469             
6470             // menu .. should add dropdown-menu class - so no need for carat..
6471             
6472             if (this.badge !== '') {
6473                  
6474                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6475             }
6476             return cfg;
6477         }
6478         
6479         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6480             cfg.cn = [
6481                 {
6482                     tag: this.tagtype,
6483                     href : this.href || "#",
6484                     html: this.html || '',
6485                     cls : ''
6486                 }
6487             ];
6488             if (this.tagtype == 'a') {
6489                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6490         
6491             }
6492             if (this.icon) {
6493                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6494             } else  if (this.fa) {
6495                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6496             } else if(this.glyphicon) {
6497                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6498             } else {
6499                 cfg.cn[0].cls += " nav-html";
6500             }
6501             
6502             if (this.menu) {
6503                 cfg.cn[0].html += " <span class='caret'></span>";
6504              
6505             }
6506             
6507             if (this.badge !== '') {
6508                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6509             }
6510         }
6511         
6512         
6513         
6514         return cfg;
6515     },
6516     onRender : function(ct, position)
6517     {
6518        // Roo.log("Call onRender: " + this.xtype);
6519         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6520             this.tag = 'div';
6521         }
6522         
6523         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6524         this.navLink = this.el.select('.nav-link',true).first();
6525         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6526         return ret;
6527     },
6528       
6529     
6530     initEvents: function() 
6531     {
6532         if (typeof (this.menu) != 'undefined') {
6533             this.menu.parentType = this.xtype;
6534             this.menu.triggerEl = this.el;
6535             this.menu = this.addxtype(Roo.apply({}, this.menu));
6536         }
6537         
6538         this.el.on('click', this.onClick, this);
6539         
6540         //if(this.tagtype == 'span'){
6541         //    this.el.select('span',true).on('click', this.onClick, this);
6542         //}
6543        
6544         // at this point parent should be available..
6545         this.parent().register(this);
6546     },
6547     
6548     onClick : function(e)
6549     {
6550         if (e.getTarget('.dropdown-menu-item')) {
6551             // did you click on a menu itemm.... - then don't trigger onclick..
6552             return;
6553         }
6554         
6555         if(
6556                 this.preventDefault ||
6557                                 this.href === false ||
6558                 this.href === '#' 
6559         ){
6560             //Roo.log("NavItem - prevent Default?");
6561             e.preventDefault();
6562         }
6563         
6564         if (this.disabled) {
6565             return;
6566         }
6567         
6568         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6569         if (tg && tg.transition) {
6570             Roo.log("waiting for the transitionend");
6571             return;
6572         }
6573         
6574         
6575         
6576         //Roo.log("fire event clicked");
6577         if(this.fireEvent('click', this, e) === false){
6578             return;
6579         };
6580         
6581         if(this.tagtype == 'span'){
6582             return;
6583         }
6584         
6585         //Roo.log(this.href);
6586         var ael = this.el.select('a',true).first();
6587         //Roo.log(ael);
6588         
6589         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6590             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6591             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6592                 return; // ignore... - it's a 'hash' to another page.
6593             }
6594             Roo.log("NavItem - prevent Default?");
6595             e.preventDefault();
6596             this.scrollToElement(e);
6597         }
6598         
6599         
6600         var p =  this.parent();
6601    
6602         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6603             if (typeof(p.setActiveItem) !== 'undefined') {
6604                 p.setActiveItem(this);
6605             }
6606         }
6607         
6608         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6609         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6610             // remove the collapsed menu expand...
6611             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6612         }
6613     },
6614     
6615     isActive: function () {
6616         return this.active
6617     },
6618     setActive : function(state, fire, is_was_active)
6619     {
6620         if (this.active && !state && this.navId) {
6621             this.was_active = true;
6622             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6623             if (nv) {
6624                 nv.clearWasActive(this);
6625             }
6626             
6627         }
6628         this.active = state;
6629         
6630         if (!state ) {
6631             this.el.removeClass('active');
6632             this.navLink ? this.navLink.removeClass('active') : false;
6633         } else if (!this.el.hasClass('active')) {
6634             
6635             this.el.addClass('active');
6636             if (Roo.bootstrap.version == 4 && this.navLink ) {
6637                 this.navLink.addClass('active');
6638             }
6639             
6640         }
6641         if (fire) {
6642             this.fireEvent('changed', this, state);
6643         }
6644         
6645         // show a panel if it's registered and related..
6646         
6647         if (!this.navId || !this.tabId || !state || is_was_active) {
6648             return;
6649         }
6650         
6651         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6652         if (!tg) {
6653             return;
6654         }
6655         var pan = tg.getPanelByName(this.tabId);
6656         if (!pan) {
6657             return;
6658         }
6659         // if we can not flip to new panel - go back to old nav highlight..
6660         if (false == tg.showPanel(pan)) {
6661             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6662             if (nv) {
6663                 var onav = nv.getWasActive();
6664                 if (onav) {
6665                     onav.setActive(true, false, true);
6666                 }
6667             }
6668             
6669         }
6670         
6671         
6672         
6673     },
6674      // this should not be here...
6675     setDisabled : function(state)
6676     {
6677         this.disabled = state;
6678         if (!state ) {
6679             this.el.removeClass('disabled');
6680         } else if (!this.el.hasClass('disabled')) {
6681             this.el.addClass('disabled');
6682         }
6683         
6684     },
6685     
6686     /**
6687      * Fetch the element to display the tooltip on.
6688      * @return {Roo.Element} defaults to this.el
6689      */
6690     tooltipEl : function()
6691     {
6692         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6693     },
6694     
6695     scrollToElement : function(e)
6696     {
6697         var c = document.body;
6698         
6699         /*
6700          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6701          */
6702         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6703             c = document.documentElement;
6704         }
6705         
6706         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6707         
6708         if(!target){
6709             return;
6710         }
6711
6712         var o = target.calcOffsetsTo(c);
6713         
6714         var options = {
6715             target : target,
6716             value : o[1]
6717         };
6718         
6719         this.fireEvent('scrollto', this, options, e);
6720         
6721         Roo.get(c).scrollTo('top', options.value, true);
6722         
6723         return;
6724     },
6725     /**
6726      * Set the HTML (text content) of the item
6727      * @param {string} html  content for the nav item
6728      */
6729     setHtml : function(html)
6730     {
6731         this.html = html;
6732         this.htmlEl.dom.innerHTML = html;
6733         
6734     } 
6735 });
6736  
6737
6738  /*
6739  * - LGPL
6740  *
6741  * sidebar item
6742  *
6743  *  li
6744  *    <span> icon </span>
6745  *    <span> text </span>
6746  *    <span>badge </span>
6747  */
6748
6749 /**
6750  * @class Roo.bootstrap.nav.SidebarItem
6751  * @extends Roo.bootstrap.nav.Item
6752  * Bootstrap Navbar.NavSidebarItem class
6753  * 
6754  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6755  * {Boolean} open is the menu open
6756  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6757  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6758  * {String} buttonSize (sm|md|lg)the extra classes for the button
6759  * {Boolean} showArrow show arrow next to the text (default true)
6760  * @constructor
6761  * Create a new Navbar Button
6762  * @param {Object} config The config object
6763  */
6764 Roo.bootstrap.nav.SidebarItem = function(config){
6765     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6766     this.addEvents({
6767         // raw events
6768         /**
6769          * @event click
6770          * The raw click event for the entire grid.
6771          * @param {Roo.EventObject} e
6772          */
6773         "click" : true,
6774          /**
6775             * @event changed
6776             * Fires when the active item active state changes
6777             * @param {Roo.bootstrap.nav.SidebarItem} this
6778             * @param {boolean} state the new state
6779              
6780          */
6781         'changed': true
6782     });
6783    
6784 };
6785
6786 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6787     
6788     badgeWeight : 'default',
6789     
6790     open: false,
6791     
6792     buttonView : false,
6793     
6794     buttonWeight : 'default',
6795     
6796     buttonSize : 'md',
6797     
6798     showArrow : true,
6799     
6800     getAutoCreate : function(){
6801         
6802         
6803         var a = {
6804                 tag: 'a',
6805                 href : this.href || '#',
6806                 cls: '',
6807                 html : '',
6808                 cn : []
6809         };
6810         
6811         if(this.buttonView){
6812             a = {
6813                 tag: 'button',
6814                 href : this.href || '#',
6815                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6816                 html : this.html,
6817                 cn : []
6818             };
6819         }
6820         
6821         var cfg = {
6822             tag: 'li',
6823             cls: '',
6824             cn: [ a ]
6825         };
6826         
6827         if (this.active) {
6828             cfg.cls += ' active';
6829         }
6830         
6831         if (this.disabled) {
6832             cfg.cls += ' disabled';
6833         }
6834         if (this.open) {
6835             cfg.cls += ' open x-open';
6836         }
6837         // left icon..
6838         if (this.glyphicon || this.icon) {
6839             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6840             a.cn.push({ tag : 'i', cls : c }) ;
6841         }
6842         
6843         if(!this.buttonView){
6844             var span = {
6845                 tag: 'span',
6846                 html : this.html || ''
6847             };
6848
6849             a.cn.push(span);
6850             
6851         }
6852         
6853         if (this.badge !== '') {
6854             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6855         }
6856         
6857         if (this.menu) {
6858             
6859             if(this.showArrow){
6860                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6861             }
6862             
6863             a.cls += ' dropdown-toggle treeview' ;
6864         }
6865         
6866         return cfg;
6867     },
6868     
6869     initEvents : function()
6870     { 
6871         if (typeof (this.menu) != 'undefined') {
6872             this.menu.parentType = this.xtype;
6873             this.menu.triggerEl = this.el;
6874             this.menu = this.addxtype(Roo.apply({}, this.menu));
6875         }
6876         
6877         this.el.on('click', this.onClick, this);
6878         
6879         if(this.badge !== ''){
6880             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6881         }
6882         
6883     },
6884     
6885     onClick : function(e)
6886     {
6887         if(this.disabled){
6888             e.preventDefault();
6889             return;
6890         }
6891         
6892         if(this.preventDefault){
6893             e.preventDefault();
6894         }
6895         
6896         this.fireEvent('click', this, e);
6897     },
6898     
6899     disable : function()
6900     {
6901         this.setDisabled(true);
6902     },
6903     
6904     enable : function()
6905     {
6906         this.setDisabled(false);
6907     },
6908     
6909     setDisabled : function(state)
6910     {
6911         if(this.disabled == state){
6912             return;
6913         }
6914         
6915         this.disabled = state;
6916         
6917         if (state) {
6918             this.el.addClass('disabled');
6919             return;
6920         }
6921         
6922         this.el.removeClass('disabled');
6923         
6924         return;
6925     },
6926     
6927     setActive : function(state)
6928     {
6929         if(this.active == state){
6930             return;
6931         }
6932         
6933         this.active = state;
6934         
6935         if (state) {
6936             this.el.addClass('active');
6937             return;
6938         }
6939         
6940         this.el.removeClass('active');
6941         
6942         return;
6943     },
6944     
6945     isActive: function () 
6946     {
6947         return this.active;
6948     },
6949     
6950     setBadge : function(str)
6951     {
6952         if(!this.badgeEl){
6953             return;
6954         }
6955         
6956         this.badgeEl.dom.innerHTML = str;
6957     }
6958     
6959    
6960      
6961  
6962 });
6963  
6964
6965  /*
6966  * - LGPL
6967  *
6968  * nav progress bar
6969  * 
6970  */
6971
6972 /**
6973  * @class Roo.bootstrap.nav.ProgressBar
6974  * @extends Roo.bootstrap.Component
6975  * @children Roo.bootstrap.nav.ProgressBarItem
6976  * Bootstrap NavProgressBar class
6977  * 
6978  * @constructor
6979  * Create a new nav progress bar - a bar indicating step along a process
6980  * @param {Object} config The config object
6981  */
6982
6983 Roo.bootstrap.nav.ProgressBar = function(config){
6984     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6985
6986     this.bullets = this.bullets || [];
6987    
6988 //    Roo.bootstrap.nav.ProgressBar.register(this);
6989      this.addEvents({
6990         /**
6991              * @event changed
6992              * Fires when the active item changes
6993              * @param {Roo.bootstrap.nav.ProgressBar} this
6994              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6995              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
6996          */
6997         'changed': true
6998      });
6999     
7000 };
7001
7002 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
7003     /**
7004      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7005      * Bullets for the Nav Progress bar for the toolbar
7006      */
7007     bullets : [],
7008     barItems : [],
7009     
7010     getAutoCreate : function()
7011     {
7012         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7013         
7014         cfg = {
7015             tag : 'div',
7016             cls : 'roo-navigation-bar-group',
7017             cn : [
7018                 {
7019                     tag : 'div',
7020                     cls : 'roo-navigation-top-bar'
7021                 },
7022                 {
7023                     tag : 'div',
7024                     cls : 'roo-navigation-bullets-bar',
7025                     cn : [
7026                         {
7027                             tag : 'ul',
7028                             cls : 'roo-navigation-bar'
7029                         }
7030                     ]
7031                 },
7032                 
7033                 {
7034                     tag : 'div',
7035                     cls : 'roo-navigation-bottom-bar'
7036                 }
7037             ]
7038             
7039         };
7040         
7041         return cfg;
7042         
7043     },
7044     
7045     initEvents: function() 
7046     {
7047         
7048     },
7049     
7050     onRender : function(ct, position) 
7051     {
7052         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7053         
7054         if(this.bullets.length){
7055             Roo.each(this.bullets, function(b){
7056                this.addItem(b);
7057             }, this);
7058         }
7059         
7060         this.format();
7061         
7062     },
7063     
7064     addItem : function(cfg)
7065     {
7066         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7067         
7068         item.parentId = this.id;
7069         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7070         
7071         if(cfg.html){
7072             var top = new Roo.bootstrap.Element({
7073                 tag : 'div',
7074                 cls : 'roo-navigation-bar-text'
7075             });
7076             
7077             var bottom = new Roo.bootstrap.Element({
7078                 tag : 'div',
7079                 cls : 'roo-navigation-bar-text'
7080             });
7081             
7082             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7083             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7084             
7085             var topText = new Roo.bootstrap.Element({
7086                 tag : 'span',
7087                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7088             });
7089             
7090             var bottomText = new Roo.bootstrap.Element({
7091                 tag : 'span',
7092                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7093             });
7094             
7095             topText.onRender(top.el, null);
7096             bottomText.onRender(bottom.el, null);
7097             
7098             item.topEl = top;
7099             item.bottomEl = bottom;
7100         }
7101         
7102         this.barItems.push(item);
7103         
7104         return item;
7105     },
7106     
7107     getActive : function()
7108     {
7109         var active = false;
7110         
7111         Roo.each(this.barItems, function(v){
7112             
7113             if (!v.isActive()) {
7114                 return;
7115             }
7116             
7117             active = v;
7118             return false;
7119             
7120         });
7121         
7122         return active;
7123     },
7124     
7125     setActiveItem : function(item)
7126     {
7127         var prev = false;
7128         
7129         Roo.each(this.barItems, function(v){
7130             if (v.rid == item.rid) {
7131                 return ;
7132             }
7133             
7134             if (v.isActive()) {
7135                 v.setActive(false);
7136                 prev = v;
7137             }
7138         });
7139
7140         item.setActive(true);
7141         
7142         this.fireEvent('changed', this, item, prev);
7143     },
7144     
7145     getBarItem: function(rid)
7146     {
7147         var ret = false;
7148         
7149         Roo.each(this.barItems, function(e) {
7150             if (e.rid != rid) {
7151                 return;
7152             }
7153             
7154             ret =  e;
7155             return false;
7156         });
7157         
7158         return ret;
7159     },
7160     
7161     indexOfItem : function(item)
7162     {
7163         var index = false;
7164         
7165         Roo.each(this.barItems, function(v, i){
7166             
7167             if (v.rid != item.rid) {
7168                 return;
7169             }
7170             
7171             index = i;
7172             return false
7173         });
7174         
7175         return index;
7176     },
7177     
7178     setActiveNext : function()
7179     {
7180         var i = this.indexOfItem(this.getActive());
7181         
7182         if (i > this.barItems.length) {
7183             return;
7184         }
7185         
7186         this.setActiveItem(this.barItems[i+1]);
7187     },
7188     
7189     setActivePrev : function()
7190     {
7191         var i = this.indexOfItem(this.getActive());
7192         
7193         if (i  < 1) {
7194             return;
7195         }
7196         
7197         this.setActiveItem(this.barItems[i-1]);
7198     },
7199     
7200     format : function()
7201     {
7202         if(!this.barItems.length){
7203             return;
7204         }
7205      
7206         var width = 100 / this.barItems.length;
7207         
7208         Roo.each(this.barItems, function(i){
7209             i.el.setStyle('width', width + '%');
7210             i.topEl.el.setStyle('width', width + '%');
7211             i.bottomEl.el.setStyle('width', width + '%');
7212         }, this);
7213         
7214     }
7215     
7216 });
7217 /*
7218  * - LGPL
7219  *
7220  * Nav Progress Item
7221  * 
7222  */
7223
7224 /**
7225  * @class Roo.bootstrap.nav.ProgressBarItem
7226  * @extends Roo.bootstrap.Component
7227  * Bootstrap NavProgressBarItem class
7228  * @cfg {String} rid the reference id
7229  * @cfg {Boolean} active (true|false) Is item active default false
7230  * @cfg {Boolean} disabled (true|false) Is item active default false
7231  * @cfg {String} html
7232  * @cfg {String} position (top|bottom) text position default bottom
7233  * @cfg {String} icon show icon instead of number
7234  * 
7235  * @constructor
7236  * Create a new NavProgressBarItem
7237  * @param {Object} config The config object
7238  */
7239 Roo.bootstrap.nav.ProgressBarItem = function(config){
7240     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7241     this.addEvents({
7242         // raw events
7243         /**
7244          * @event click
7245          * The raw click event for the entire grid.
7246          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7247          * @param {Roo.EventObject} e
7248          */
7249         "click" : true
7250     });
7251    
7252 };
7253
7254 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7255     
7256     rid : '',
7257     active : false,
7258     disabled : false,
7259     html : '',
7260     position : 'bottom',
7261     icon : false,
7262     
7263     getAutoCreate : function()
7264     {
7265         var iconCls = 'roo-navigation-bar-item-icon';
7266         
7267         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7268         
7269         var cfg = {
7270             tag: 'li',
7271             cls: 'roo-navigation-bar-item',
7272             cn : [
7273                 {
7274                     tag : 'i',
7275                     cls : iconCls
7276                 }
7277             ]
7278         };
7279         
7280         if(this.active){
7281             cfg.cls += ' active';
7282         }
7283         if(this.disabled){
7284             cfg.cls += ' disabled';
7285         }
7286         
7287         return cfg;
7288     },
7289     
7290     disable : function()
7291     {
7292         this.setDisabled(true);
7293     },
7294     
7295     enable : function()
7296     {
7297         this.setDisabled(false);
7298     },
7299     
7300     initEvents: function() 
7301     {
7302         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7303         
7304         this.iconEl.on('click', this.onClick, this);
7305     },
7306     
7307     onClick : function(e)
7308     {
7309         e.preventDefault();
7310         
7311         if(this.disabled){
7312             return;
7313         }
7314         
7315         if(this.fireEvent('click', this, e) === false){
7316             return;
7317         };
7318         
7319         this.parent().setActiveItem(this);
7320     },
7321     
7322     isActive: function () 
7323     {
7324         return this.active;
7325     },
7326     
7327     setActive : function(state)
7328     {
7329         if(this.active == state){
7330             return;
7331         }
7332         
7333         this.active = state;
7334         
7335         if (state) {
7336             this.el.addClass('active');
7337             return;
7338         }
7339         
7340         this.el.removeClass('active');
7341         
7342         return;
7343     },
7344     
7345     setDisabled : function(state)
7346     {
7347         if(this.disabled == state){
7348             return;
7349         }
7350         
7351         this.disabled = state;
7352         
7353         if (state) {
7354             this.el.addClass('disabled');
7355             return;
7356         }
7357         
7358         this.el.removeClass('disabled');
7359     },
7360     
7361     tooltipEl : function()
7362     {
7363         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7364     }
7365 });
7366  
7367
7368  /*
7369  * - LGPL
7370  *
7371  *  Breadcrumb Nav
7372  * 
7373  */
7374 Roo.namespace('Roo.bootstrap.breadcrumb');
7375
7376
7377 /**
7378  * @class Roo.bootstrap.breadcrumb.Nav
7379  * @extends Roo.bootstrap.Component
7380  * Bootstrap Breadcrumb Nav Class
7381  *  
7382  * @children Roo.bootstrap.breadcrumb.Item
7383  * 
7384  * @constructor
7385  * Create a new breadcrumb.Nav
7386  * @param {Object} config The config object
7387  */
7388
7389
7390 Roo.bootstrap.breadcrumb.Nav = function(config){
7391     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7392     
7393     
7394 };
7395
7396 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7397     
7398     getAutoCreate : function()
7399     {
7400
7401         var cfg = {
7402             tag: 'nav',
7403             cn : [
7404                 {
7405                     tag : 'ol',
7406                     cls : 'breadcrumb'
7407                 }
7408             ]
7409             
7410         };
7411           
7412         return cfg;
7413     },
7414     
7415     initEvents: function()
7416     {
7417         this.olEl = this.el.select('ol',true).first();    
7418     },
7419     getChildContainer : function()
7420     {
7421         return this.olEl;  
7422     }
7423     
7424 });
7425
7426  /*
7427  * - LGPL
7428  *
7429  *  Breadcrumb Item
7430  * 
7431  */
7432
7433
7434 /**
7435  * @class Roo.bootstrap.breadcrumb.Nav
7436  * @extends Roo.bootstrap.Component
7437  * @children Roo.bootstrap.Component
7438  * @parent Roo.bootstrap.breadcrumb.Nav
7439  * Bootstrap Breadcrumb Nav Class
7440  *  
7441  * 
7442  * @cfg {String} html the content of the link.
7443  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7444  * @cfg {Boolean} active is it active
7445
7446  * 
7447  * @constructor
7448  * Create a new breadcrumb.Nav
7449  * @param {Object} config The config object
7450  */
7451
7452 Roo.bootstrap.breadcrumb.Item = function(config){
7453     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7454     this.addEvents({
7455         // img events
7456         /**
7457          * @event click
7458          * The img click event for the img.
7459          * @param {Roo.EventObject} e
7460          */
7461         "click" : true
7462     });
7463     
7464 };
7465
7466 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7467     
7468     href: false,
7469     html : '',
7470     
7471     getAutoCreate : function()
7472     {
7473
7474         var cfg = {
7475             tag: 'li',
7476             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7477         };
7478         if (this.href !== false) {
7479             cfg.cn = [{
7480                 tag : 'a',
7481                 href : this.href,
7482                 html : this.html
7483             }];
7484         } else {
7485             cfg.html = this.html;
7486         }
7487         
7488         return cfg;
7489     },
7490     
7491     initEvents: function()
7492     {
7493         if (this.href) {
7494             this.el.select('a', true).first().on('click',this.onClick, this)
7495         }
7496         
7497     },
7498     onClick : function(e)
7499     {
7500         e.preventDefault();
7501         this.fireEvent('click',this,  e);
7502     }
7503     
7504 });
7505
7506  /*
7507  * - LGPL
7508  *
7509  * row
7510  * 
7511  */
7512
7513 /**
7514  * @class Roo.bootstrap.Row
7515  * @extends Roo.bootstrap.Component
7516  * @children Roo.bootstrap.Component
7517  * Bootstrap Row class (contains columns...)
7518  * 
7519  * @constructor
7520  * Create a new Row
7521  * @param {Object} config The config object
7522  */
7523
7524 Roo.bootstrap.Row = function(config){
7525     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7526 };
7527
7528 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7529     
7530     getAutoCreate : function(){
7531        return {
7532             cls: 'row clearfix'
7533        };
7534     }
7535     
7536     
7537 });
7538
7539  
7540
7541  /*
7542  * - LGPL
7543  *
7544  * pagination
7545  * 
7546  */
7547
7548 /**
7549  * @class Roo.bootstrap.Pagination
7550  * @extends Roo.bootstrap.Component
7551  * @children Roo.bootstrap.Pagination
7552  * Bootstrap Pagination class
7553  * 
7554  * @cfg {String} size (xs|sm|md|lg|xl)
7555  * @cfg {Boolean} inverse 
7556  * 
7557  * @constructor
7558  * Create a new Pagination
7559  * @param {Object} config The config object
7560  */
7561
7562 Roo.bootstrap.Pagination = function(config){
7563     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7564 };
7565
7566 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7567     
7568     cls: false,
7569     size: false,
7570     inverse: false,
7571     
7572     getAutoCreate : function(){
7573         var cfg = {
7574             tag: 'ul',
7575                 cls: 'pagination'
7576         };
7577         if (this.inverse) {
7578             cfg.cls += ' inverse';
7579         }
7580         if (this.html) {
7581             cfg.html=this.html;
7582         }
7583         if (this.cls) {
7584             cfg.cls += " " + this.cls;
7585         }
7586         return cfg;
7587     }
7588    
7589 });
7590
7591  
7592
7593  /*
7594  * - LGPL
7595  *
7596  * Pagination item
7597  * 
7598  */
7599
7600
7601 /**
7602  * @class Roo.bootstrap.PaginationItem
7603  * @extends Roo.bootstrap.Component
7604  * Bootstrap PaginationItem class
7605  * @cfg {String} html text
7606  * @cfg {String} href the link
7607  * @cfg {Boolean} preventDefault (true | false) default true
7608  * @cfg {Boolean} active (true | false) default false
7609  * @cfg {Boolean} disabled default false
7610  * 
7611  * 
7612  * @constructor
7613  * Create a new PaginationItem
7614  * @param {Object} config The config object
7615  */
7616
7617
7618 Roo.bootstrap.PaginationItem = function(config){
7619     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7620     this.addEvents({
7621         // raw events
7622         /**
7623          * @event click
7624          * The raw click event for the entire grid.
7625          * @param {Roo.EventObject} e
7626          */
7627         "click" : true
7628     });
7629 };
7630
7631 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7632     
7633     href : false,
7634     html : false,
7635     preventDefault: true,
7636     active : false,
7637     cls : false,
7638     disabled: false,
7639     
7640     getAutoCreate : function(){
7641         var cfg= {
7642             tag: 'li',
7643             cn: [
7644                 {
7645                     tag : 'a',
7646                     href : this.href ? this.href : '#',
7647                     html : this.html ? this.html : ''
7648                 }
7649             ]
7650         };
7651         
7652         if(this.cls){
7653             cfg.cls = this.cls;
7654         }
7655         
7656         if(this.disabled){
7657             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7658         }
7659         
7660         if(this.active){
7661             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7662         }
7663         
7664         return cfg;
7665     },
7666     
7667     initEvents: function() {
7668         
7669         this.el.on('click', this.onClick, this);
7670         
7671     },
7672     onClick : function(e)
7673     {
7674         Roo.log('PaginationItem on click ');
7675         if(this.preventDefault){
7676             e.preventDefault();
7677         }
7678         
7679         if(this.disabled){
7680             return;
7681         }
7682         
7683         this.fireEvent('click', this, e);
7684     }
7685    
7686 });
7687
7688  
7689
7690  /*
7691  * - LGPL
7692  *
7693  * slider
7694  * 
7695  */
7696
7697
7698 /**
7699  * @class Roo.bootstrap.Slider
7700  * @extends Roo.bootstrap.Component
7701  * Bootstrap Slider class
7702  *    
7703  * @constructor
7704  * Create a new Slider
7705  * @param {Object} config The config object
7706  */
7707
7708 Roo.bootstrap.Slider = function(config){
7709     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7710 };
7711
7712 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7713     
7714     getAutoCreate : function(){
7715         
7716         var cfg = {
7717             tag: 'div',
7718             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7719             cn: [
7720                 {
7721                     tag: 'a',
7722                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7723                 }
7724             ]
7725         };
7726         
7727         return cfg;
7728     }
7729    
7730 });
7731
7732  /*
7733  * Based on:
7734  * Ext JS Library 1.1.1
7735  * Copyright(c) 2006-2007, Ext JS, LLC.
7736  *
7737  * Originally Released Under LGPL - original licence link has changed is not relivant.
7738  *
7739  * Fork - LGPL
7740  * <script type="text/javascript">
7741  */
7742  /**
7743  * @extends Roo.dd.DDProxy
7744  * @class Roo.grid.SplitDragZone
7745  * Support for Column Header resizing
7746  * @constructor
7747  * @param {Object} config
7748  */
7749 // private
7750 // This is a support class used internally by the Grid components
7751 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7752     this.grid = grid;
7753     this.view = grid.getView();
7754     this.proxy = this.view.resizeProxy;
7755     Roo.grid.SplitDragZone.superclass.constructor.call(
7756         this,
7757         hd, // ID
7758         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7759         {  // CONFIG
7760             dragElId : Roo.id(this.proxy.dom),
7761             resizeFrame:false
7762         }
7763     );
7764     
7765     this.setHandleElId(Roo.id(hd));
7766     if (hd2 !== false) {
7767         this.setOuterHandleElId(Roo.id(hd2));
7768     }
7769     
7770     this.scroll = false;
7771 };
7772 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7773     fly: Roo.Element.fly,
7774
7775     b4StartDrag : function(x, y){
7776         this.view.headersDisabled = true;
7777         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7778                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7779         );
7780         this.proxy.setHeight(h);
7781         
7782         // for old system colWidth really stored the actual width?
7783         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7784         // which in reality did not work.. - it worked only for fixed sizes
7785         // for resizable we need to use actual sizes.
7786         var w = this.cm.getColumnWidth(this.cellIndex);
7787         if (!this.view.mainWrap) {
7788             // bootstrap.
7789             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7790         }
7791         
7792         
7793         
7794         // this was w-this.grid.minColumnWidth;
7795         // doesnt really make sense? - w = thie curren width or the rendered one?
7796         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7797         this.resetConstraints();
7798         this.setXConstraint(minw, 1000);
7799         this.setYConstraint(0, 0);
7800         this.minX = x - minw;
7801         this.maxX = x + 1000;
7802         this.startPos = x;
7803         if (!this.view.mainWrap) { // this is Bootstrap code..
7804             this.getDragEl().style.display='block';
7805         }
7806         
7807         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7808     },
7809
7810
7811     handleMouseDown : function(e){
7812         ev = Roo.EventObject.setEvent(e);
7813         var t = this.fly(ev.getTarget());
7814         if(t.hasClass("x-grid-split")){
7815             this.cellIndex = this.view.getCellIndex(t.dom);
7816             this.split = t.dom;
7817             this.cm = this.grid.colModel;
7818             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7819                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7820             }
7821         }
7822     },
7823
7824     endDrag : function(e){
7825         this.view.headersDisabled = false;
7826         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7827         var diff = endX - this.startPos;
7828         // 
7829         var w = this.cm.getColumnWidth(this.cellIndex);
7830         if (!this.view.mainWrap) {
7831             w = 0;
7832         }
7833         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7834     },
7835
7836     autoOffset : function(){
7837         this.setDelta(0,0);
7838     }
7839 });/*
7840  * Based on:
7841  * Ext JS Library 1.1.1
7842  * Copyright(c) 2006-2007, Ext JS, LLC.
7843  *
7844  * Originally Released Under LGPL - original licence link has changed is not relivant.
7845  *
7846  * Fork - LGPL
7847  * <script type="text/javascript">
7848  */
7849
7850 /**
7851  * @class Roo.grid.AbstractSelectionModel
7852  * @extends Roo.util.Observable
7853  * @abstract
7854  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7855  * implemented by descendant classes.  This class should not be directly instantiated.
7856  * @constructor
7857  */
7858 Roo.grid.AbstractSelectionModel = function(){
7859     this.locked = false;
7860     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7861 };
7862
7863 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7864     /** @ignore Called by the grid automatically. Do not call directly. */
7865     init : function(grid){
7866         this.grid = grid;
7867         this.initEvents();
7868     },
7869
7870     /**
7871      * Locks the selections.
7872      */
7873     lock : function(){
7874         this.locked = true;
7875     },
7876
7877     /**
7878      * Unlocks the selections.
7879      */
7880     unlock : function(){
7881         this.locked = false;
7882     },
7883
7884     /**
7885      * Returns true if the selections are locked.
7886      * @return {Boolean}
7887      */
7888     isLocked : function(){
7889         return this.locked;
7890     }
7891 });/*
7892  * Based on:
7893  * Ext JS Library 1.1.1
7894  * Copyright(c) 2006-2007, Ext JS, LLC.
7895  *
7896  * Originally Released Under LGPL - original licence link has changed is not relivant.
7897  *
7898  * Fork - LGPL
7899  * <script type="text/javascript">
7900  */
7901 /**
7902  * @extends Roo.grid.AbstractSelectionModel
7903  * @class Roo.grid.RowSelectionModel
7904  * The default SelectionModel used by {@link Roo.grid.Grid}.
7905  * It supports multiple selections and keyboard selection/navigation. 
7906  * @constructor
7907  * @param {Object} config
7908  */
7909 Roo.grid.RowSelectionModel = function(config){
7910     Roo.apply(this, config);
7911     this.selections = new Roo.util.MixedCollection(false, function(o){
7912         return o.id;
7913     });
7914
7915     this.last = false;
7916     this.lastActive = false;
7917
7918     this.addEvents({
7919         /**
7920         * @event selectionchange
7921         * Fires when the selection changes
7922         * @param {SelectionModel} this
7923         */
7924        "selectionchange" : true,
7925        /**
7926         * @event afterselectionchange
7927         * Fires after the selection changes (eg. by key press or clicking)
7928         * @param {SelectionModel} this
7929         */
7930        "afterselectionchange" : true,
7931        /**
7932         * @event beforerowselect
7933         * Fires when a row is selected being selected, return false to cancel.
7934         * @param {SelectionModel} this
7935         * @param {Number} rowIndex The selected index
7936         * @param {Boolean} keepExisting False if other selections will be cleared
7937         */
7938        "beforerowselect" : true,
7939        /**
7940         * @event rowselect
7941         * Fires when a row is selected.
7942         * @param {SelectionModel} this
7943         * @param {Number} rowIndex The selected index
7944         * @param {Roo.data.Record} r The record
7945         */
7946        "rowselect" : true,
7947        /**
7948         * @event rowdeselect
7949         * Fires when a row is deselected.
7950         * @param {SelectionModel} this
7951         * @param {Number} rowIndex The selected index
7952         */
7953         "rowdeselect" : true
7954     });
7955     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7956     this.locked = false;
7957 };
7958
7959 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7960     /**
7961      * @cfg {Boolean} singleSelect
7962      * True to allow selection of only one row at a time (defaults to false)
7963      */
7964     singleSelect : false,
7965
7966     // private
7967     initEvents : function(){
7968
7969         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7970             this.grid.on("mousedown", this.handleMouseDown, this);
7971         }else{ // allow click to work like normal
7972             this.grid.on("rowclick", this.handleDragableRowClick, this);
7973         }
7974         // bootstrap does not have a view..
7975         var view = this.grid.view ? this.grid.view : this.grid;
7976         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7977             "up" : function(e){
7978                 if(!e.shiftKey){
7979                     this.selectPrevious(e.shiftKey);
7980                 }else if(this.last !== false && this.lastActive !== false){
7981                     var last = this.last;
7982                     this.selectRange(this.last,  this.lastActive-1);
7983                     view.focusRow(this.lastActive);
7984                     if(last !== false){
7985                         this.last = last;
7986                     }
7987                 }else{
7988                     this.selectFirstRow();
7989                 }
7990                 this.fireEvent("afterselectionchange", this);
7991             },
7992             "down" : function(e){
7993                 if(!e.shiftKey){
7994                     this.selectNext(e.shiftKey);
7995                 }else if(this.last !== false && this.lastActive !== false){
7996                     var last = this.last;
7997                     this.selectRange(this.last,  this.lastActive+1);
7998                     view.focusRow(this.lastActive);
7999                     if(last !== false){
8000                         this.last = last;
8001                     }
8002                 }else{
8003                     this.selectFirstRow();
8004                 }
8005                 this.fireEvent("afterselectionchange", this);
8006             },
8007             scope: this
8008         });
8009
8010          
8011         view.on("refresh", this.onRefresh, this);
8012         view.on("rowupdated", this.onRowUpdated, this);
8013         view.on("rowremoved", this.onRemove, this);
8014     },
8015
8016     // private
8017     onRefresh : function(){
8018         var ds = this.grid.ds, i, v = this.grid.view;
8019         var s = this.selections;
8020         s.each(function(r){
8021             if((i = ds.indexOfId(r.id)) != -1){
8022                 v.onRowSelect(i);
8023                 s.add(ds.getAt(i)); // updating the selection relate data
8024             }else{
8025                 s.remove(r);
8026             }
8027         });
8028     },
8029
8030     // private
8031     onRemove : function(v, index, r){
8032         this.selections.remove(r);
8033     },
8034
8035     // private
8036     onRowUpdated : function(v, index, r){
8037         if(this.isSelected(r)){
8038             v.onRowSelect(index);
8039         }
8040     },
8041
8042     /**
8043      * Select records.
8044      * @param {Array} records The records to select
8045      * @param {Boolean} keepExisting (optional) True to keep existing selections
8046      */
8047     selectRecords : function(records, keepExisting){
8048         if(!keepExisting){
8049             this.clearSelections();
8050         }
8051         var ds = this.grid.ds;
8052         for(var i = 0, len = records.length; i < len; i++){
8053             this.selectRow(ds.indexOf(records[i]), true);
8054         }
8055     },
8056
8057     /**
8058      * Gets the number of selected rows.
8059      * @return {Number}
8060      */
8061     getCount : function(){
8062         return this.selections.length;
8063     },
8064
8065     /**
8066      * Selects the first row in the grid.
8067      */
8068     selectFirstRow : function(){
8069         this.selectRow(0);
8070     },
8071
8072     /**
8073      * Select the last row.
8074      * @param {Boolean} keepExisting (optional) True to keep existing selections
8075      */
8076     selectLastRow : function(keepExisting){
8077         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8078     },
8079
8080     /**
8081      * Selects the row immediately following the last selected row.
8082      * @param {Boolean} keepExisting (optional) True to keep existing selections
8083      */
8084     selectNext : function(keepExisting){
8085         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8086             this.selectRow(this.last+1, keepExisting);
8087             var view = this.grid.view ? this.grid.view : this.grid;
8088             view.focusRow(this.last);
8089         }
8090     },
8091
8092     /**
8093      * Selects the row that precedes the last selected row.
8094      * @param {Boolean} keepExisting (optional) True to keep existing selections
8095      */
8096     selectPrevious : function(keepExisting){
8097         if(this.last){
8098             this.selectRow(this.last-1, keepExisting);
8099             var view = this.grid.view ? this.grid.view : this.grid;
8100             view.focusRow(this.last);
8101         }
8102     },
8103
8104     /**
8105      * Returns the selected records
8106      * @return {Array} Array of selected records
8107      */
8108     getSelections : function(){
8109         return [].concat(this.selections.items);
8110     },
8111
8112     /**
8113      * Returns the first selected record.
8114      * @return {Record}
8115      */
8116     getSelected : function(){
8117         return this.selections.itemAt(0);
8118     },
8119
8120
8121     /**
8122      * Clears all selections.
8123      */
8124     clearSelections : function(fast){
8125         if(this.locked) {
8126             return;
8127         }
8128         if(fast !== true){
8129             var ds = this.grid.ds;
8130             var s = this.selections;
8131             s.each(function(r){
8132                 this.deselectRow(ds.indexOfId(r.id));
8133             }, this);
8134             s.clear();
8135         }else{
8136             this.selections.clear();
8137         }
8138         this.last = false;
8139     },
8140
8141
8142     /**
8143      * Selects all rows.
8144      */
8145     selectAll : function(){
8146         if(this.locked) {
8147             return;
8148         }
8149         this.selections.clear();
8150         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8151             this.selectRow(i, true);
8152         }
8153     },
8154
8155     /**
8156      * Returns True if there is a selection.
8157      * @return {Boolean}
8158      */
8159     hasSelection : function(){
8160         return this.selections.length > 0;
8161     },
8162
8163     /**
8164      * Returns True if the specified row is selected.
8165      * @param {Number/Record} record The record or index of the record to check
8166      * @return {Boolean}
8167      */
8168     isSelected : function(index){
8169         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8170         return (r && this.selections.key(r.id) ? true : false);
8171     },
8172
8173     /**
8174      * Returns True if the specified record id is selected.
8175      * @param {String} id The id of record to check
8176      * @return {Boolean}
8177      */
8178     isIdSelected : function(id){
8179         return (this.selections.key(id) ? true : false);
8180     },
8181
8182     // private
8183     handleMouseDown : function(e, t)
8184     {
8185         var view = this.grid.view ? this.grid.view : this.grid;
8186         var rowIndex;
8187         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8188             return;
8189         };
8190         if(e.shiftKey && this.last !== false){
8191             var last = this.last;
8192             this.selectRange(last, rowIndex, e.ctrlKey);
8193             this.last = last; // reset the last
8194             view.focusRow(rowIndex);
8195         }else{
8196             var isSelected = this.isSelected(rowIndex);
8197             if(e.button !== 0 && isSelected){
8198                 view.focusRow(rowIndex);
8199             }else if(e.ctrlKey && isSelected){
8200                 this.deselectRow(rowIndex);
8201             }else if(!isSelected){
8202                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8203                 view.focusRow(rowIndex);
8204             }
8205         }
8206         this.fireEvent("afterselectionchange", this);
8207     },
8208     // private
8209     handleDragableRowClick :  function(grid, rowIndex, e) 
8210     {
8211         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8212             this.selectRow(rowIndex, false);
8213             var view = this.grid.view ? this.grid.view : this.grid;
8214             view.focusRow(rowIndex);
8215              this.fireEvent("afterselectionchange", this);
8216         }
8217     },
8218     
8219     /**
8220      * Selects multiple rows.
8221      * @param {Array} rows Array of the indexes of the row to select
8222      * @param {Boolean} keepExisting (optional) True to keep existing selections
8223      */
8224     selectRows : function(rows, keepExisting){
8225         if(!keepExisting){
8226             this.clearSelections();
8227         }
8228         for(var i = 0, len = rows.length; i < len; i++){
8229             this.selectRow(rows[i], true);
8230         }
8231     },
8232
8233     /**
8234      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8235      * @param {Number} startRow The index of the first row in the range
8236      * @param {Number} endRow The index of the last row in the range
8237      * @param {Boolean} keepExisting (optional) True to retain existing selections
8238      */
8239     selectRange : function(startRow, endRow, keepExisting){
8240         if(this.locked) {
8241             return;
8242         }
8243         if(!keepExisting){
8244             this.clearSelections();
8245         }
8246         if(startRow <= endRow){
8247             for(var i = startRow; i <= endRow; i++){
8248                 this.selectRow(i, true);
8249             }
8250         }else{
8251             for(var i = startRow; i >= endRow; i--){
8252                 this.selectRow(i, true);
8253             }
8254         }
8255     },
8256
8257     /**
8258      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8259      * @param {Number} startRow The index of the first row in the range
8260      * @param {Number} endRow The index of the last row in the range
8261      */
8262     deselectRange : function(startRow, endRow, preventViewNotify){
8263         if(this.locked) {
8264             return;
8265         }
8266         for(var i = startRow; i <= endRow; i++){
8267             this.deselectRow(i, preventViewNotify);
8268         }
8269     },
8270
8271     /**
8272      * Selects a row.
8273      * @param {Number} row The index of the row to select
8274      * @param {Boolean} keepExisting (optional) True to keep existing selections
8275      */
8276     selectRow : function(index, keepExisting, preventViewNotify){
8277         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8278             return;
8279         }
8280         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8281             if(!keepExisting || this.singleSelect){
8282                 this.clearSelections();
8283             }
8284             var r = this.grid.ds.getAt(index);
8285             this.selections.add(r);
8286             this.last = this.lastActive = index;
8287             if(!preventViewNotify){
8288                 var view = this.grid.view ? this.grid.view : this.grid;
8289                 view.onRowSelect(index);
8290             }
8291             this.fireEvent("rowselect", this, index, r);
8292             this.fireEvent("selectionchange", this);
8293         }
8294     },
8295
8296     /**
8297      * Deselects a row.
8298      * @param {Number} row The index of the row to deselect
8299      */
8300     deselectRow : function(index, preventViewNotify){
8301         if(this.locked) {
8302             return;
8303         }
8304         if(this.last == index){
8305             this.last = false;
8306         }
8307         if(this.lastActive == index){
8308             this.lastActive = false;
8309         }
8310         var r = this.grid.ds.getAt(index);
8311         this.selections.remove(r);
8312         if(!preventViewNotify){
8313             var view = this.grid.view ? this.grid.view : this.grid;
8314             view.onRowDeselect(index);
8315         }
8316         this.fireEvent("rowdeselect", this, index);
8317         this.fireEvent("selectionchange", this);
8318     },
8319
8320     // private
8321     restoreLast : function(){
8322         if(this._last){
8323             this.last = this._last;
8324         }
8325     },
8326
8327     // private
8328     acceptsNav : function(row, col, cm){
8329         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8330     },
8331
8332     // private
8333     onEditorKey : function(field, e){
8334         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8335         if(k == e.TAB){
8336             e.stopEvent();
8337             ed.completeEdit();
8338             if(e.shiftKey){
8339                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8340             }else{
8341                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8342             }
8343         }else if(k == e.ENTER && !e.ctrlKey){
8344             e.stopEvent();
8345             ed.completeEdit();
8346             if(e.shiftKey){
8347                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8348             }else{
8349                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8350             }
8351         }else if(k == e.ESC){
8352             ed.cancelEdit();
8353         }
8354         if(newCell){
8355             g.startEditing(newCell[0], newCell[1]);
8356         }
8357     }
8358 });/*
8359  * Based on:
8360  * Ext JS Library 1.1.1
8361  * Copyright(c) 2006-2007, Ext JS, LLC.
8362  *
8363  * Originally Released Under LGPL - original licence link has changed is not relivant.
8364  *
8365  * Fork - LGPL
8366  * <script type="text/javascript">
8367  */
8368  
8369
8370 /**
8371  * @class Roo.grid.ColumnModel
8372  * @extends Roo.util.Observable
8373  * This is the default implementation of a ColumnModel used by the Grid. It defines
8374  * the columns in the grid.
8375  * <br>Usage:<br>
8376  <pre><code>
8377  var colModel = new Roo.grid.ColumnModel([
8378         {header: "Ticker", width: 60, sortable: true, locked: true},
8379         {header: "Company Name", width: 150, sortable: true},
8380         {header: "Market Cap.", width: 100, sortable: true},
8381         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8382         {header: "Employees", width: 100, sortable: true, resizable: false}
8383  ]);
8384  </code></pre>
8385  * <p>
8386  
8387  * The config options listed for this class are options which may appear in each
8388  * individual column definition.
8389  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8390  * @constructor
8391  * @param {Object} config An Array of column config objects. See this class's
8392  * config objects for details.
8393 */
8394 Roo.grid.ColumnModel = function(config){
8395         /**
8396      * The config passed into the constructor
8397      */
8398     this.config = []; //config;
8399     this.lookup = {};
8400
8401     // if no id, create one
8402     // if the column does not have a dataIndex mapping,
8403     // map it to the order it is in the config
8404     for(var i = 0, len = config.length; i < len; i++){
8405         this.addColumn(config[i]);
8406         
8407     }
8408
8409     /**
8410      * The width of columns which have no width specified (defaults to 100)
8411      * @type Number
8412      */
8413     this.defaultWidth = 100;
8414
8415     /**
8416      * Default sortable of columns which have no sortable specified (defaults to false)
8417      * @type Boolean
8418      */
8419     this.defaultSortable = false;
8420
8421     this.addEvents({
8422         /**
8423              * @event widthchange
8424              * Fires when the width of a column changes.
8425              * @param {ColumnModel} this
8426              * @param {Number} columnIndex The column index
8427              * @param {Number} newWidth The new width
8428              */
8429             "widthchange": true,
8430         /**
8431              * @event headerchange
8432              * Fires when the text of a header changes.
8433              * @param {ColumnModel} this
8434              * @param {Number} columnIndex The column index
8435              * @param {Number} newText The new header text
8436              */
8437             "headerchange": true,
8438         /**
8439              * @event hiddenchange
8440              * Fires when a column is hidden or "unhidden".
8441              * @param {ColumnModel} this
8442              * @param {Number} columnIndex The column index
8443              * @param {Boolean} hidden true if hidden, false otherwise
8444              */
8445             "hiddenchange": true,
8446             /**
8447          * @event columnmoved
8448          * Fires when a column is moved.
8449          * @param {ColumnModel} this
8450          * @param {Number} oldIndex
8451          * @param {Number} newIndex
8452          */
8453         "columnmoved" : true,
8454         /**
8455          * @event columlockchange
8456          * Fires when a column's locked state is changed
8457          * @param {ColumnModel} this
8458          * @param {Number} colIndex
8459          * @param {Boolean} locked true if locked
8460          */
8461         "columnlockchange" : true
8462     });
8463     Roo.grid.ColumnModel.superclass.constructor.call(this);
8464 };
8465 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8466     /**
8467      * @cfg {String} header [required] The header text to display in the Grid view.
8468      */
8469         /**
8470      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8471      */
8472         /**
8473      * @cfg {String} smHeader Header at Bootsrap Small width
8474      */
8475         /**
8476      * @cfg {String} mdHeader Header at Bootsrap Medium width
8477      */
8478         /**
8479      * @cfg {String} lgHeader Header at Bootsrap Large width
8480      */
8481         /**
8482      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8483      */
8484     /**
8485      * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
8486      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8487      * specified, the column's index is used as an index into the Record's data Array.
8488      */
8489     /**
8490      * @cfg {Number} width  The initial width in pixels of the column. Using this
8491      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8492      */
8493     /**
8494      * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
8495      * Defaults to the value of the {@link #defaultSortable} property.
8496      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8497      */
8498     /**
8499      * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
8500      */
8501     /**
8502      * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
8503      */
8504     /**
8505      * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
8506      */
8507     /**
8508      * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
8509      */
8510     /**
8511      * @cfg {Function} renderer A function used to generate HTML markup for a cell
8512      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8513      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8514      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8515      */
8516        /**
8517      * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
8518      */
8519     /**
8520      * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
8521      */
8522     /**
8523      * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
8524      */
8525     /**
8526      * @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)
8527      */
8528     /**
8529      * @cfg {String} tooltip mouse over tooltip text
8530      */
8531     /**
8532      * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
8533      */
8534     /**
8535      * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
8536      */
8537     /**
8538      * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
8539      */
8540     /**
8541      * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
8542      */
8543         /**
8544      * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
8545      */
8546     /**
8547      * Returns the id of the column at the specified index.
8548      * @param {Number} index The column index
8549      * @return {String} the id
8550      */
8551     getColumnId : function(index){
8552         return this.config[index].id;
8553     },
8554
8555     /**
8556      * Returns the column for a specified id.
8557      * @param {String} id The column id
8558      * @return {Object} the column
8559      */
8560     getColumnById : function(id){
8561         return this.lookup[id];
8562     },
8563
8564     
8565     /**
8566      * Returns the column Object for a specified dataIndex.
8567      * @param {String} dataIndex The column dataIndex
8568      * @return {Object|Boolean} the column or false if not found
8569      */
8570     getColumnByDataIndex: function(dataIndex){
8571         var index = this.findColumnIndex(dataIndex);
8572         return index > -1 ? this.config[index] : false;
8573     },
8574     
8575     /**
8576      * Returns the index for a specified column id.
8577      * @param {String} id The column id
8578      * @return {Number} the index, or -1 if not found
8579      */
8580     getIndexById : function(id){
8581         for(var i = 0, len = this.config.length; i < len; i++){
8582             if(this.config[i].id == id){
8583                 return i;
8584             }
8585         }
8586         return -1;
8587     },
8588     
8589     /**
8590      * Returns the index for a specified column dataIndex.
8591      * @param {String} dataIndex The column dataIndex
8592      * @return {Number} the index, or -1 if not found
8593      */
8594     
8595     findColumnIndex : function(dataIndex){
8596         for(var i = 0, len = this.config.length; i < len; i++){
8597             if(this.config[i].dataIndex == dataIndex){
8598                 return i;
8599             }
8600         }
8601         return -1;
8602     },
8603     
8604     
8605     moveColumn : function(oldIndex, newIndex){
8606         var c = this.config[oldIndex];
8607         this.config.splice(oldIndex, 1);
8608         this.config.splice(newIndex, 0, c);
8609         this.dataMap = null;
8610         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8611     },
8612
8613     isLocked : function(colIndex){
8614         return this.config[colIndex].locked === true;
8615     },
8616
8617     setLocked : function(colIndex, value, suppressEvent){
8618         if(this.isLocked(colIndex) == value){
8619             return;
8620         }
8621         this.config[colIndex].locked = value;
8622         if(!suppressEvent){
8623             this.fireEvent("columnlockchange", this, colIndex, value);
8624         }
8625     },
8626
8627     getTotalLockedWidth : function(){
8628         var totalWidth = 0;
8629         for(var i = 0; i < this.config.length; i++){
8630             if(this.isLocked(i) && !this.isHidden(i)){
8631                 this.totalWidth += this.getColumnWidth(i);
8632             }
8633         }
8634         return totalWidth;
8635     },
8636
8637     getLockedCount : function(){
8638         for(var i = 0, len = this.config.length; i < len; i++){
8639             if(!this.isLocked(i)){
8640                 return i;
8641             }
8642         }
8643         
8644         return this.config.length;
8645     },
8646
8647     /**
8648      * Returns the number of columns.
8649      * @return {Number}
8650      */
8651     getColumnCount : function(visibleOnly){
8652         if(visibleOnly === true){
8653             var c = 0;
8654             for(var i = 0, len = this.config.length; i < len; i++){
8655                 if(!this.isHidden(i)){
8656                     c++;
8657                 }
8658             }
8659             return c;
8660         }
8661         return this.config.length;
8662     },
8663
8664     /**
8665      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8666      * @param {Function} fn
8667      * @param {Object} scope (optional)
8668      * @return {Array} result
8669      */
8670     getColumnsBy : function(fn, scope){
8671         var r = [];
8672         for(var i = 0, len = this.config.length; i < len; i++){
8673             var c = this.config[i];
8674             if(fn.call(scope||this, c, i) === true){
8675                 r[r.length] = c;
8676             }
8677         }
8678         return r;
8679     },
8680
8681     /**
8682      * Returns true if the specified column is sortable.
8683      * @param {Number} col The column index
8684      * @return {Boolean}
8685      */
8686     isSortable : function(col){
8687         if(typeof this.config[col].sortable == "undefined"){
8688             return this.defaultSortable;
8689         }
8690         return this.config[col].sortable;
8691     },
8692
8693     /**
8694      * Returns the rendering (formatting) function defined for the column.
8695      * @param {Number} col The column index.
8696      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8697      */
8698     getRenderer : function(col){
8699         if(!this.config[col].renderer){
8700             return Roo.grid.ColumnModel.defaultRenderer;
8701         }
8702         return this.config[col].renderer;
8703     },
8704
8705     /**
8706      * Sets the rendering (formatting) function for a column.
8707      * @param {Number} col The column index
8708      * @param {Function} fn The function to use to process the cell's raw data
8709      * to return HTML markup for the grid view. The render function is called with
8710      * the following parameters:<ul>
8711      * <li>Data value.</li>
8712      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8713      * <li>css A CSS style string to apply to the table cell.</li>
8714      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8715      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8716      * <li>Row index</li>
8717      * <li>Column index</li>
8718      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8719      */
8720     setRenderer : function(col, fn){
8721         this.config[col].renderer = fn;
8722     },
8723
8724     /**
8725      * Returns the width for the specified column.
8726      * @param {Number} col The column index
8727      * @param (optional) {String} gridSize bootstrap width size.
8728      * @return {Number}
8729      */
8730     getColumnWidth : function(col, gridSize)
8731         {
8732                 var cfg = this.config[col];
8733                 
8734                 if (typeof(gridSize) == 'undefined') {
8735                         return cfg.width * 1 || this.defaultWidth;
8736                 }
8737                 if (gridSize === false) { // if we set it..
8738                         return cfg.width || false;
8739                 }
8740                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8741                 
8742                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8743                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8744                                 continue;
8745                         }
8746                         return cfg[ sizes[i] ];
8747                 }
8748                 return 1;
8749                 
8750     },
8751
8752     /**
8753      * Sets the width for a column.
8754      * @param {Number} col The column index
8755      * @param {Number} width The new width
8756      */
8757     setColumnWidth : function(col, width, suppressEvent){
8758         this.config[col].width = width;
8759         this.totalWidth = null;
8760         if(!suppressEvent){
8761              this.fireEvent("widthchange", this, col, width);
8762         }
8763     },
8764
8765     /**
8766      * Returns the total width of all columns.
8767      * @param {Boolean} includeHidden True to include hidden column widths
8768      * @return {Number}
8769      */
8770     getTotalWidth : function(includeHidden){
8771         if(!this.totalWidth){
8772             this.totalWidth = 0;
8773             for(var i = 0, len = this.config.length; i < len; i++){
8774                 if(includeHidden || !this.isHidden(i)){
8775                     this.totalWidth += this.getColumnWidth(i);
8776                 }
8777             }
8778         }
8779         return this.totalWidth;
8780     },
8781
8782     /**
8783      * Returns the header for the specified column.
8784      * @param {Number} col The column index
8785      * @return {String}
8786      */
8787     getColumnHeader : function(col){
8788         return this.config[col].header;
8789     },
8790
8791     /**
8792      * Sets the header for a column.
8793      * @param {Number} col The column index
8794      * @param {String} header The new header
8795      */
8796     setColumnHeader : function(col, header){
8797         this.config[col].header = header;
8798         this.fireEvent("headerchange", this, col, header);
8799     },
8800
8801     /**
8802      * Returns the tooltip for the specified column.
8803      * @param {Number} col The column index
8804      * @return {String}
8805      */
8806     getColumnTooltip : function(col){
8807             return this.config[col].tooltip;
8808     },
8809     /**
8810      * Sets the tooltip for a column.
8811      * @param {Number} col The column index
8812      * @param {String} tooltip The new tooltip
8813      */
8814     setColumnTooltip : function(col, tooltip){
8815             this.config[col].tooltip = tooltip;
8816     },
8817
8818     /**
8819      * Returns the dataIndex for the specified column.
8820      * @param {Number} col The column index
8821      * @return {Number}
8822      */
8823     getDataIndex : function(col){
8824         return this.config[col].dataIndex;
8825     },
8826
8827     /**
8828      * Sets the dataIndex for a column.
8829      * @param {Number} col The column index
8830      * @param {Number} dataIndex The new dataIndex
8831      */
8832     setDataIndex : function(col, dataIndex){
8833         this.config[col].dataIndex = dataIndex;
8834     },
8835
8836     
8837     
8838     /**
8839      * Returns true if the cell is editable.
8840      * @param {Number} colIndex The column index
8841      * @param {Number} rowIndex The row index - this is nto actually used..?
8842      * @return {Boolean}
8843      */
8844     isCellEditable : function(colIndex, rowIndex){
8845         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8846     },
8847
8848     /**
8849      * Returns the editor defined for the cell/column.
8850      * return false or null to disable editing.
8851      * @param {Number} colIndex The column index
8852      * @param {Number} rowIndex The row index
8853      * @return {Object}
8854      */
8855     getCellEditor : function(colIndex, rowIndex){
8856         return this.config[colIndex].editor;
8857     },
8858
8859     /**
8860      * Sets if a column is editable.
8861      * @param {Number} col The column index
8862      * @param {Boolean} editable True if the column is editable
8863      */
8864     setEditable : function(col, editable){
8865         this.config[col].editable = editable;
8866     },
8867
8868
8869     /**
8870      * Returns true if the column is hidden.
8871      * @param {Number} colIndex The column index
8872      * @return {Boolean}
8873      */
8874     isHidden : function(colIndex){
8875         return this.config[colIndex].hidden;
8876     },
8877
8878
8879     /**
8880      * Returns true if the column width cannot be changed
8881      */
8882     isFixed : function(colIndex){
8883         return this.config[colIndex].fixed;
8884     },
8885
8886     /**
8887      * Returns true if the column can be resized
8888      * @return {Boolean}
8889      */
8890     isResizable : function(colIndex){
8891         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8892     },
8893     /**
8894      * Sets if a column is hidden.
8895      * @param {Number} colIndex The column index
8896      * @param {Boolean} hidden True if the column is hidden
8897      */
8898     setHidden : function(colIndex, hidden){
8899         this.config[colIndex].hidden = hidden;
8900         this.totalWidth = null;
8901         this.fireEvent("hiddenchange", this, colIndex, hidden);
8902     },
8903
8904     /**
8905      * Sets the editor for a column.
8906      * @param {Number} col The column index
8907      * @param {Object} editor The editor object
8908      */
8909     setEditor : function(col, editor){
8910         this.config[col].editor = editor;
8911     },
8912     /**
8913      * Add a column (experimental...) - defaults to adding to the end..
8914      * @param {Object} config 
8915     */
8916     addColumn : function(c)
8917     {
8918     
8919         var i = this.config.length;
8920         this.config[i] = c;
8921         
8922         if(typeof c.dataIndex == "undefined"){
8923             c.dataIndex = i;
8924         }
8925         if(typeof c.renderer == "string"){
8926             c.renderer = Roo.util.Format[c.renderer];
8927         }
8928         if(typeof c.id == "undefined"){
8929             c.id = Roo.id();
8930         }
8931         if(c.editor && c.editor.xtype){
8932             c.editor  = Roo.factory(c.editor, Roo.grid);
8933         }
8934         if(c.editor && c.editor.isFormField){
8935             c.editor = new Roo.grid.GridEditor(c.editor);
8936         }
8937         this.lookup[c.id] = c;
8938     }
8939     
8940 });
8941
8942 Roo.grid.ColumnModel.defaultRenderer = function(value)
8943 {
8944     if(typeof value == "object") {
8945         return value;
8946     }
8947         if(typeof value == "string" && value.length < 1){
8948             return "&#160;";
8949         }
8950     
8951         return String.format("{0}", value);
8952 };
8953
8954 // Alias for backwards compatibility
8955 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8956 /*
8957  * Based on:
8958  * Ext JS Library 1.1.1
8959  * Copyright(c) 2006-2007, Ext JS, LLC.
8960  *
8961  * Originally Released Under LGPL - original licence link has changed is not relivant.
8962  *
8963  * Fork - LGPL
8964  * <script type="text/javascript">
8965  */
8966  
8967 /**
8968  * @class Roo.LoadMask
8969  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8970  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8971  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8972  * element's UpdateManager load indicator and will be destroyed after the initial load.
8973  * @constructor
8974  * Create a new LoadMask
8975  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8976  * @param {Object} config The config object
8977  */
8978 Roo.LoadMask = function(el, config){
8979     this.el = Roo.get(el);
8980     Roo.apply(this, config);
8981     if(this.store){
8982         this.store.on('beforeload', this.onBeforeLoad, this);
8983         this.store.on('load', this.onLoad, this);
8984         this.store.on('loadexception', this.onLoadException, this);
8985         this.removeMask = false;
8986     }else{
8987         var um = this.el.getUpdateManager();
8988         um.showLoadIndicator = false; // disable the default indicator
8989         um.on('beforeupdate', this.onBeforeLoad, this);
8990         um.on('update', this.onLoad, this);
8991         um.on('failure', this.onLoad, this);
8992         this.removeMask = true;
8993     }
8994 };
8995
8996 Roo.LoadMask.prototype = {
8997     /**
8998      * @cfg {Boolean} removeMask
8999      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
9000      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
9001      */
9002     removeMask : false,
9003     /**
9004      * @cfg {String} msg
9005      * The text to display in a centered loading message box (defaults to 'Loading...')
9006      */
9007     msg : 'Loading...',
9008     /**
9009      * @cfg {String} msgCls
9010      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9011      */
9012     msgCls : 'x-mask-loading',
9013
9014     /**
9015      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9016      * @type Boolean
9017      */
9018     disabled: false,
9019
9020     /**
9021      * Disables the mask to prevent it from being displayed
9022      */
9023     disable : function(){
9024        this.disabled = true;
9025     },
9026
9027     /**
9028      * Enables the mask so that it can be displayed
9029      */
9030     enable : function(){
9031         this.disabled = false;
9032     },
9033     
9034     onLoadException : function()
9035     {
9036         Roo.log(arguments);
9037         
9038         if (typeof(arguments[3]) != 'undefined') {
9039             Roo.MessageBox.alert("Error loading",arguments[3]);
9040         } 
9041         /*
9042         try {
9043             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9044                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9045             }   
9046         } catch(e) {
9047             
9048         }
9049         */
9050     
9051         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9052     },
9053     // private
9054     onLoad : function()
9055     {
9056         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9057     },
9058
9059     // private
9060     onBeforeLoad : function(){
9061         if(!this.disabled){
9062             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9063         }
9064     },
9065
9066     // private
9067     destroy : function(){
9068         if(this.store){
9069             this.store.un('beforeload', this.onBeforeLoad, this);
9070             this.store.un('load', this.onLoad, this);
9071             this.store.un('loadexception', this.onLoadException, this);
9072         }else{
9073             var um = this.el.getUpdateManager();
9074             um.un('beforeupdate', this.onBeforeLoad, this);
9075             um.un('update', this.onLoad, this);
9076             um.un('failure', this.onLoad, this);
9077         }
9078     }
9079 };/**
9080  * @class Roo.bootstrap.Table
9081  * @licence LGBL
9082  * @extends Roo.bootstrap.Component
9083  * @children Roo.bootstrap.TableBody
9084  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9085  * Similar to Roo.grid.Grid
9086  * <pre><code>
9087  var table = Roo.factory({
9088     xtype : 'Table',
9089     xns : Roo.bootstrap,
9090     autoSizeColumns: true,
9091     
9092     
9093     store : {
9094         xtype : 'Store',
9095         xns : Roo.data,
9096         remoteSort : true,
9097         sortInfo : { direction : 'ASC', field: 'name' },
9098         proxy : {
9099            xtype : 'HttpProxy',
9100            xns : Roo.data,
9101            method : 'GET',
9102            url : 'https://example.com/some.data.url.json'
9103         },
9104         reader : {
9105            xtype : 'JsonReader',
9106            xns : Roo.data,
9107            fields : [ 'id', 'name', whatever' ],
9108            id : 'id',
9109            root : 'data'
9110         }
9111     },
9112     cm : [
9113         {
9114             xtype : 'ColumnModel',
9115             xns : Roo.grid,
9116             align : 'center',
9117             cursor : 'pointer',
9118             dataIndex : 'is_in_group',
9119             header : "Name",
9120             sortable : true,
9121             renderer : function(v, x , r) {  
9122             
9123                 return String.format("{0}", v)
9124             }
9125             width : 3
9126         } // more columns..
9127     ],
9128     selModel : {
9129         xtype : 'RowSelectionModel',
9130         xns : Roo.bootstrap.Table
9131         // you can add listeners to catch selection change here....
9132     }
9133      
9134
9135  });
9136  // set any options
9137  grid.render(Roo.get("some-div"));
9138 </code></pre>
9139
9140 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9141
9142
9143
9144  *
9145  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9146  * @cfg {Roo.data.Store} store The data store to use
9147  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9148  * 
9149  * @cfg {String} cls table class
9150  *
9151  *
9152  * @cfg {string} empty_results  Text to display for no results 
9153  * @cfg {boolean} striped Should the rows be alternative striped
9154  * @cfg {boolean} bordered Add borders to the table
9155  * @cfg {boolean} hover Add hover highlighting
9156  * @cfg {boolean} condensed Format condensed
9157  * @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,
9158  *                also adds table-responsive (see bootstrap docs for details)
9159  * @cfg {Boolean} loadMask (true|false) default false
9160  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9161  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9162  * @cfg {Boolean} rowSelection (true|false) default false
9163  * @cfg {Boolean} cellSelection (true|false) default false
9164  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9165  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9166  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9167  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9168  * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
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   
9347     rowSelection : false,
9348     cellSelection : false,
9349     layout : false,
9350
9351     minColumnWidth : 50,
9352     
9353     // Roo.Element - the tbody
9354     bodyEl: false,  // <tbody> Roo.Element - thead element    
9355     headEl: false,  // <thead> Roo.Element - thead element
9356     resizeProxy : false, // proxy element for dragging?
9357
9358
9359     
9360     container: false, // used by gridpanel...
9361     
9362     lazyLoad : false,
9363     
9364     CSS : Roo.util.CSS,
9365     
9366     auto_hide_footer : false,
9367     
9368     view: false, // actually points to this..
9369     
9370     getAutoCreate : function()
9371     {
9372         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9373         
9374         cfg = {
9375             tag: 'table',
9376             cls : 'table', 
9377             cn : []
9378         };
9379         // this get's auto added by panel.Grid
9380         if (this.scrollBody) {
9381             cfg.cls += ' table-body-fixed';
9382         }    
9383         if (this.striped) {
9384             cfg.cls += ' table-striped';
9385         }
9386         
9387         if (this.hover) {
9388             cfg.cls += ' table-hover';
9389         }
9390         if (this.bordered) {
9391             cfg.cls += ' table-bordered';
9392         }
9393         if (this.condensed) {
9394             cfg.cls += ' table-condensed';
9395         }
9396         
9397         if (this.responsive) {
9398             cfg.cls += ' table-responsive';
9399         }
9400         
9401         if (this.cls) {
9402             cfg.cls+=  ' ' +this.cls;
9403         }
9404         
9405         
9406         
9407         if (this.layout) {
9408             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9409         }
9410         
9411         if(this.store || this.cm){
9412             if(this.headerShow){
9413                 cfg.cn.push(this.renderHeader());
9414             }
9415             
9416             cfg.cn.push(this.renderBody());
9417             
9418             if(this.footerShow){
9419                 cfg.cn.push(this.renderFooter());
9420             }
9421             // where does this come from?
9422             //cfg.cls+=  ' TableGrid';
9423         }
9424         
9425         return { cn : [ cfg ] };
9426     },
9427     
9428     initEvents : function()
9429     {   
9430         if(!this.store || !this.cm){
9431             return;
9432         }
9433         if (this.selModel) {
9434             this.selModel.initEvents();
9435         }
9436         
9437         
9438         //Roo.log('initEvents with ds!!!!');
9439         
9440         this.bodyEl = this.el.select('tbody', true).first();
9441         this.headEl = this.el.select('thead', true).first();
9442         this.mainFoot = this.el.select('tfoot', true).first();
9443         
9444         
9445         
9446         
9447         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9448             e.on('click', this.sort, this);
9449         }, this);
9450         
9451         
9452         // why is this done????? = it breaks dialogs??
9453         //this.parent().el.setStyle('position', 'relative');
9454         
9455         
9456         if (this.footer) {
9457             this.footer.parentId = this.id;
9458             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9459             
9460             if(this.lazyLoad){
9461                 this.el.select('tfoot tr td').first().addClass('hide');
9462             }
9463         } 
9464         
9465         if(this.loadMask) {
9466             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9467         }
9468         
9469         this.store.on('load', this.onLoad, this);
9470         this.store.on('beforeload', this.onBeforeLoad, this);
9471         this.store.on('update', this.onUpdate, this);
9472         this.store.on('add', this.onAdd, this);
9473         this.store.on("clear", this.clear, this);
9474         
9475         this.el.on("contextmenu", this.onContextMenu, this);
9476         
9477         
9478         this.cm.on("headerchange", this.onHeaderChange, this);
9479         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9480
9481  //?? does bodyEl get replaced on render?
9482         this.bodyEl.on("click", this.onClick, this);
9483         this.bodyEl.on("dblclick", this.onDblClick, this);        
9484         this.bodyEl.on('scroll', this.onBodyScroll, this);
9485
9486         // guessing mainbody will work - this relays usually caught by selmodel at present.
9487         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9488   
9489   
9490         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9491         
9492   
9493         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9494             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9495         }
9496         
9497         this.initCSS();
9498     },
9499     // Compatibility with grid - we implement all the view features at present.
9500     getView : function()
9501     {
9502         return this;
9503     },
9504     
9505     initCSS : function()
9506     {
9507         
9508         
9509         var cm = this.cm, styles = [];
9510         this.CSS.removeStyleSheet(this.id + '-cssrules');
9511         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9512         // we can honour xs/sm/md/xl  as widths...
9513         // we first have to decide what widht we are currently at...
9514         var sz = Roo.getGridSize();
9515         
9516         var total = 0;
9517         var last = -1;
9518         var cols = []; // visable cols.
9519         var total_abs = 0;
9520         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9521             var w = cm.getColumnWidth(i, false);
9522             if(cm.isHidden(i)){
9523                 cols.push( { rel : false, abs : 0 });
9524                 continue;
9525             }
9526             if (w !== false) {
9527                 cols.push( { rel : false, abs : w });
9528                 total_abs += w;
9529                 last = i; // not really..
9530                 continue;
9531             }
9532             var w = cm.getColumnWidth(i, sz);
9533             if (w > 0) {
9534                 last = i
9535             }
9536             total += w;
9537             cols.push( { rel : w, abs : false });
9538         }
9539         
9540         var avail = this.bodyEl.dom.clientWidth - total_abs;
9541         
9542         var unitWidth = Math.floor(avail / total);
9543         var rem = avail - (unitWidth * total);
9544         
9545         var hidden, width, pos = 0 , splithide , left;
9546         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9547             
9548             hidden = 'display:none;';
9549             left = '';
9550             width  = 'width:0px;';
9551             splithide = '';
9552             if(!cm.isHidden(i)){
9553                 hidden = '';
9554                 
9555                 
9556                 // we can honour xs/sm/md/xl ?
9557                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9558                 if (w===0) {
9559                     hidden = 'display:none;';
9560                 }
9561                 // width should return a small number...
9562                 if (i == last) {
9563                     w+=rem; // add the remaining with..
9564                 }
9565                 pos += w;
9566                 left = "left:" + (pos -4) + "px;";
9567                 width = "width:" + w+ "px;";
9568                 
9569             }
9570             if (this.responsive) {
9571                 width = '';
9572                 left = '';
9573                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9574                 splithide = 'display: none;';
9575             }
9576             
9577             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9578             if (this.headEl) {
9579                 if (i == last) {
9580                     splithide = 'display:none;';
9581                 }
9582                 
9583                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9584                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9585                             // this is the popover version..
9586                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9587                 );
9588             }
9589             
9590         }
9591         //Roo.log(styles.join(''));
9592         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9593         
9594     },
9595     
9596     
9597     
9598     onContextMenu : function(e, t)
9599     {
9600         this.processEvent("contextmenu", e);
9601     },
9602     
9603     processEvent : function(name, e)
9604     {
9605         if (name != 'touchstart' ) {
9606             this.fireEvent(name, e);    
9607         }
9608         
9609         var t = e.getTarget();
9610         
9611         var cell = Roo.get(t);
9612         
9613         if(!cell){
9614             return;
9615         }
9616         
9617         if(cell.findParent('tfoot', false, true)){
9618             return;
9619         }
9620         
9621         if(cell.findParent('thead', false, true)){
9622             
9623             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9624                 cell = Roo.get(t).findParent('th', false, true);
9625                 if (!cell) {
9626                     Roo.log("failed to find th in thead?");
9627                     Roo.log(e.getTarget());
9628                     return;
9629                 }
9630             }
9631             
9632             var cellIndex = cell.dom.cellIndex;
9633             
9634             var ename = name == 'touchstart' ? 'click' : name;
9635             this.fireEvent("header" + ename, this, cellIndex, e);
9636             
9637             return;
9638         }
9639         
9640         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9641             cell = Roo.get(t).findParent('td', false, true);
9642             if (!cell) {
9643                 Roo.log("failed to find th in tbody?");
9644                 Roo.log(e.getTarget());
9645                 return;
9646             }
9647         }
9648         
9649         var row = cell.findParent('tr', false, true);
9650         var cellIndex = cell.dom.cellIndex;
9651         var rowIndex = row.dom.rowIndex - 1;
9652         
9653         if(row !== false){
9654             
9655             this.fireEvent("row" + name, this, rowIndex, e);
9656             
9657             if(cell !== false){
9658             
9659                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9660             }
9661         }
9662         
9663     },
9664     
9665     onMouseover : function(e, el)
9666     {
9667         var cell = Roo.get(el);
9668         
9669         if(!cell){
9670             return;
9671         }
9672         
9673         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9674             cell = cell.findParent('td', false, true);
9675         }
9676         
9677         var row = cell.findParent('tr', false, true);
9678         var cellIndex = cell.dom.cellIndex;
9679         var rowIndex = row.dom.rowIndex - 1; // start from 0
9680         
9681         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9682         
9683     },
9684     
9685     onMouseout : function(e, el)
9686     {
9687         var cell = Roo.get(el);
9688         
9689         if(!cell){
9690             return;
9691         }
9692         
9693         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9694             cell = cell.findParent('td', false, true);
9695         }
9696         
9697         var row = cell.findParent('tr', false, true);
9698         var cellIndex = cell.dom.cellIndex;
9699         var rowIndex = row.dom.rowIndex - 1; // start from 0
9700         
9701         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9702         
9703     },
9704     
9705     onClick : function(e, el)
9706     {
9707         var cell = Roo.get(el);
9708         
9709         if(!cell || (!this.cellSelection && !this.rowSelection)){
9710             return;
9711         }
9712         
9713         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9714             cell = cell.findParent('td', false, true);
9715         }
9716         
9717         if(!cell || typeof(cell) == 'undefined'){
9718             return;
9719         }
9720         
9721         var row = cell.findParent('tr', false, true);
9722         
9723         if(!row || typeof(row) == 'undefined'){
9724             return;
9725         }
9726         
9727         var cellIndex = cell.dom.cellIndex;
9728         var rowIndex = this.getRowIndex(row);
9729         
9730         // why??? - should these not be based on SelectionModel?
9731         //if(this.cellSelection){
9732             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9733         //}
9734         
9735         //if(this.rowSelection){
9736             this.fireEvent('rowclick', this, row, rowIndex, e);
9737         //}
9738          
9739     },
9740         
9741     onDblClick : function(e,el)
9742     {
9743         var cell = Roo.get(el);
9744         
9745         if(!cell || (!this.cellSelection && !this.rowSelection)){
9746             return;
9747         }
9748         
9749         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9750             cell = cell.findParent('td', false, true);
9751         }
9752         
9753         if(!cell || typeof(cell) == 'undefined'){
9754             return;
9755         }
9756         
9757         var row = cell.findParent('tr', false, true);
9758         
9759         if(!row || typeof(row) == 'undefined'){
9760             return;
9761         }
9762         
9763         var cellIndex = cell.dom.cellIndex;
9764         var rowIndex = this.getRowIndex(row);
9765         
9766         if(this.cellSelection){
9767             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9768         }
9769         
9770         if(this.rowSelection){
9771             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9772         }
9773     },
9774     findRowIndex : function(el)
9775     {
9776         var cell = Roo.get(el);
9777         if(!cell) {
9778             return false;
9779         }
9780         var row = cell.findParent('tr', false, true);
9781         
9782         if(!row || typeof(row) == 'undefined'){
9783             return false;
9784         }
9785         return this.getRowIndex(row);
9786     },
9787     sort : function(e,el)
9788     {
9789         var col = Roo.get(el);
9790         
9791         if(!col.hasClass('sortable')){
9792             return;
9793         }
9794         
9795         var sort = col.attr('sort');
9796         var dir = 'ASC';
9797         
9798         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9799             dir = 'DESC';
9800         }
9801         
9802         this.store.sortInfo = {field : sort, direction : dir};
9803         
9804         if (this.footer) {
9805             Roo.log("calling footer first");
9806             this.footer.onClick('first');
9807         } else {
9808         
9809             this.store.load({ params : { start : 0 } });
9810         }
9811     },
9812     
9813     renderHeader : function()
9814     {
9815         var header = {
9816             tag: 'thead',
9817             cn : []
9818         };
9819         
9820         var cm = this.cm;
9821         this.totalWidth = 0;
9822         
9823         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9824             
9825             var config = cm.config[i];
9826             
9827             var c = {
9828                 tag: 'th',
9829                 cls : 'x-hcol-' + i,
9830                 style : '',
9831                 
9832                 html: cm.getColumnHeader(i)
9833             };
9834             
9835             var tooltip = cm.getColumnTooltip(i);
9836             if (tooltip) {
9837                 c.tooltip = tooltip;
9838             }
9839             
9840             
9841             var hh = '';
9842             
9843             if(typeof(config.sortable) != 'undefined' && config.sortable){
9844                 c.cls += ' sortable';
9845                 c.html = '<i class="fa"></i>' + c.html;
9846             }
9847             
9848             // could use BS4 hidden-..-down 
9849             
9850             if(typeof(config.lgHeader) != 'undefined'){
9851                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9852             }
9853             
9854             if(typeof(config.mdHeader) != 'undefined'){
9855                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9856             }
9857             
9858             if(typeof(config.smHeader) != 'undefined'){
9859                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9860             }
9861             
9862             if(typeof(config.xsHeader) != 'undefined'){
9863                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9864             }
9865             
9866             if(hh.length){
9867                 c.html = hh;
9868             }
9869             
9870             if(typeof(config.tooltip) != 'undefined'){
9871                 c.tooltip = config.tooltip;
9872             }
9873             
9874             if(typeof(config.colspan) != 'undefined'){
9875                 c.colspan = config.colspan;
9876             }
9877             
9878             // hidden is handled by CSS now
9879             
9880             if(typeof(config.dataIndex) != 'undefined'){
9881                 c.sort = config.dataIndex;
9882             }
9883             
9884            
9885             
9886             if(typeof(config.align) != 'undefined' && config.align.length){
9887                 c.style += ' text-align:' + config.align + ';';
9888             }
9889             
9890             /* width is done in CSS
9891              *if(typeof(config.width) != 'undefined'){
9892                 c.style += ' width:' + config.width + 'px;';
9893                 this.totalWidth += config.width;
9894             } else {
9895                 this.totalWidth += 100; // assume minimum of 100 per column?
9896             }
9897             */
9898             
9899             if(typeof(config.cls) != 'undefined'){
9900                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9901             }
9902             // this is the bit that doesnt reall work at all...
9903             
9904             if (this.responsive) {
9905                  
9906             
9907                 ['xs','sm','md','lg'].map(function(size){
9908                     
9909                     if(typeof(config[size]) == 'undefined'){
9910                         return;
9911                     }
9912                      
9913                     if (!config[size]) { // 0 = hidden
9914                         // BS 4 '0' is treated as hide that column and below.
9915                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9916                         return;
9917                     }
9918                     
9919                     c.cls += ' col-' + size + '-' + config[size] + (
9920                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9921                     );
9922                     
9923                     
9924                 });
9925             }
9926             // at the end?
9927             
9928             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9929             
9930             
9931             
9932             
9933             header.cn.push(c)
9934         }
9935         
9936         return header;
9937     },
9938     
9939     renderBody : function()
9940     {
9941         var body = {
9942             tag: 'tbody',
9943             cn : [
9944                 {
9945                     tag: 'tr',
9946                     cn : [
9947                         {
9948                             tag : 'td',
9949                             colspan :  this.cm.getColumnCount()
9950                         }
9951                     ]
9952                 }
9953             ]
9954         };
9955         
9956         return body;
9957     },
9958     
9959     renderFooter : function()
9960     {
9961         var footer = {
9962             tag: 'tfoot',
9963             cn : [
9964                 {
9965                     tag: 'tr',
9966                     cn : [
9967                         {
9968                             tag : 'td',
9969                             colspan :  this.cm.getColumnCount()
9970                         }
9971                     ]
9972                 }
9973             ]
9974         };
9975         
9976         return footer;
9977     },
9978     
9979     
9980     
9981     onLoad : function()
9982     {
9983 //        Roo.log('ds onload');
9984         this.clear();
9985         
9986         var _this = this;
9987         var cm = this.cm;
9988         var ds = this.store;
9989         
9990         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9991             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9992             if (_this.store.sortInfo) {
9993                     
9994                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9995                     e.select('i', true).addClass(['fa-arrow-up']);
9996                 }
9997                 
9998                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9999                     e.select('i', true).addClass(['fa-arrow-down']);
10000                 }
10001             }
10002         });
10003         
10004         var tbody =  this.bodyEl;
10005               
10006         if(ds.getCount() > 0){
10007             ds.data.each(function(d,rowIndex){
10008                 var row =  this.renderRow(cm, ds, rowIndex);
10009                 
10010                 tbody.createChild(row);
10011                 
10012                 var _this = this;
10013                 
10014                 if(row.cellObjects.length){
10015                     Roo.each(row.cellObjects, function(r){
10016                         _this.renderCellObject(r);
10017                     })
10018                 }
10019                 
10020             }, this);
10021         } else if (this.empty_results.length) {
10022             this.el.mask(this.empty_results, 'no-spinner');
10023         }
10024         
10025         var tfoot = this.el.select('tfoot', true).first();
10026         
10027         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10028             
10029             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10030             
10031             var total = this.ds.getTotalCount();
10032             
10033             if(this.footer.pageSize < total){
10034                 this.mainFoot.show();
10035             }
10036         }
10037         
10038         Roo.each(this.el.select('tbody td', true).elements, function(e){
10039             e.on('mouseover', _this.onMouseover, _this);
10040         });
10041         
10042         Roo.each(this.el.select('tbody td', true).elements, function(e){
10043             e.on('mouseout', _this.onMouseout, _this);
10044         });
10045         this.fireEvent('rowsrendered', this);
10046         
10047         this.autoSize();
10048         
10049         this.initCSS(); /// resize cols
10050
10051         
10052     },
10053     
10054     
10055     onUpdate : function(ds,record)
10056     {
10057         this.refreshRow(record);
10058         this.autoSize();
10059     },
10060     
10061     onRemove : function(ds, record, index, isUpdate){
10062         if(isUpdate !== true){
10063             this.fireEvent("beforerowremoved", this, index, record);
10064         }
10065         var bt = this.bodyEl.dom;
10066         
10067         var rows = this.el.select('tbody > tr', true).elements;
10068         
10069         if(typeof(rows[index]) != 'undefined'){
10070             bt.removeChild(rows[index].dom);
10071         }
10072         
10073 //        if(bt.rows[index]){
10074 //            bt.removeChild(bt.rows[index]);
10075 //        }
10076         
10077         if(isUpdate !== true){
10078             //this.stripeRows(index);
10079             //this.syncRowHeights(index, index);
10080             //this.layout();
10081             this.fireEvent("rowremoved", this, index, record);
10082         }
10083     },
10084     
10085     onAdd : function(ds, records, rowIndex)
10086     {
10087         //Roo.log('on Add called');
10088         // - note this does not handle multiple adding very well..
10089         var bt = this.bodyEl.dom;
10090         for (var i =0 ; i < records.length;i++) {
10091             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10092             //Roo.log(records[i]);
10093             //Roo.log(this.store.getAt(rowIndex+i));
10094             this.insertRow(this.store, rowIndex + i, false);
10095             return;
10096         }
10097         
10098     },
10099     
10100     
10101     refreshRow : function(record){
10102         var ds = this.store, index;
10103         if(typeof record == 'number'){
10104             index = record;
10105             record = ds.getAt(index);
10106         }else{
10107             index = ds.indexOf(record);
10108             if (index < 0) {
10109                 return; // should not happen - but seems to 
10110             }
10111         }
10112         this.insertRow(ds, index, true);
10113         this.autoSize();
10114         this.onRemove(ds, record, index+1, true);
10115         this.autoSize();
10116         //this.syncRowHeights(index, index);
10117         //this.layout();
10118         this.fireEvent("rowupdated", this, index, record);
10119     },
10120     // private - called by RowSelection
10121     onRowSelect : function(rowIndex){
10122         var row = this.getRowDom(rowIndex);
10123         row.addClass(['bg-info','info']);
10124     },
10125     // private - called by RowSelection
10126     onRowDeselect : function(rowIndex)
10127     {
10128         if (rowIndex < 0) {
10129             return;
10130         }
10131         var row = this.getRowDom(rowIndex);
10132         row.removeClass(['bg-info','info']);
10133     },
10134       /**
10135      * Focuses the specified row.
10136      * @param {Number} row The row index
10137      */
10138     focusRow : function(row)
10139     {
10140         //Roo.log('GridView.focusRow');
10141         var x = this.bodyEl.dom.scrollLeft;
10142         this.focusCell(row, 0, false);
10143         this.bodyEl.dom.scrollLeft = x;
10144
10145     },
10146      /**
10147      * Focuses the specified cell.
10148      * @param {Number} row The row index
10149      * @param {Number} col The column index
10150      * @param {Boolean} hscroll false to disable horizontal scrolling
10151      */
10152     focusCell : function(row, col, hscroll)
10153     {
10154         //Roo.log('GridView.focusCell');
10155         var el = this.ensureVisible(row, col, hscroll);
10156         // not sure what focusEL achives = it's a <a> pos relative 
10157         //this.focusEl.alignTo(el, "tl-tl");
10158         //if(Roo.isGecko){
10159         //    this.focusEl.focus();
10160         //}else{
10161         //    this.focusEl.focus.defer(1, this.focusEl);
10162         //}
10163     },
10164     
10165      /**
10166      * Scrolls the specified cell into view
10167      * @param {Number} row The row index
10168      * @param {Number} col The column index
10169      * @param {Boolean} hscroll false to disable horizontal scrolling
10170      */
10171     ensureVisible : function(row, col, hscroll)
10172     {
10173         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10174         //return null; //disable for testing.
10175         if(typeof row != "number"){
10176             row = row.rowIndex;
10177         }
10178         if(row < 0 && row >= this.ds.getCount()){
10179             return  null;
10180         }
10181         col = (col !== undefined ? col : 0);
10182         var cm = this.cm;
10183         while(cm.isHidden(col)){
10184             col++;
10185         }
10186
10187         var el = this.getCellDom(row, col);
10188         if(!el){
10189             return null;
10190         }
10191         var c = this.bodyEl.dom;
10192
10193         var ctop = parseInt(el.offsetTop, 10);
10194         var cleft = parseInt(el.offsetLeft, 10);
10195         var cbot = ctop + el.offsetHeight;
10196         var cright = cleft + el.offsetWidth;
10197
10198         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10199         var ch = 0; //?? header is not withing the area?
10200         var stop = parseInt(c.scrollTop, 10);
10201         var sleft = parseInt(c.scrollLeft, 10);
10202         var sbot = stop + ch;
10203         var sright = sleft + c.clientWidth;
10204         /*
10205         Roo.log('GridView.ensureVisible:' +
10206                 ' ctop:' + ctop +
10207                 ' c.clientHeight:' + c.clientHeight +
10208                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10209                 ' stop:' + stop +
10210                 ' cbot:' + cbot +
10211                 ' sbot:' + sbot +
10212                 ' ch:' + ch  
10213                 );
10214         */
10215         if(ctop < stop){
10216             c.scrollTop = ctop;
10217             //Roo.log("set scrolltop to ctop DISABLE?");
10218         }else if(cbot > sbot){
10219             //Roo.log("set scrolltop to cbot-ch");
10220             c.scrollTop = cbot-ch;
10221         }
10222
10223         if(hscroll !== false){
10224             if(cleft < sleft){
10225                 c.scrollLeft = cleft;
10226             }else if(cright > sright){
10227                 c.scrollLeft = cright-c.clientWidth;
10228             }
10229         }
10230
10231         return el;
10232     },
10233     
10234     
10235     insertRow : function(dm, rowIndex, isUpdate){
10236         
10237         if(!isUpdate){
10238             this.fireEvent("beforerowsinserted", this, rowIndex);
10239         }
10240             //var s = this.getScrollState();
10241         var row = this.renderRow(this.cm, this.store, rowIndex);
10242         // insert before rowIndex..
10243         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10244         
10245         var _this = this;
10246                 
10247         if(row.cellObjects.length){
10248             Roo.each(row.cellObjects, function(r){
10249                 _this.renderCellObject(r);
10250             })
10251         }
10252             
10253         if(!isUpdate){
10254             this.fireEvent("rowsinserted", this, rowIndex);
10255             //this.syncRowHeights(firstRow, lastRow);
10256             //this.stripeRows(firstRow);
10257             //this.layout();
10258         }
10259         
10260     },
10261     
10262     
10263     getRowDom : function(rowIndex)
10264     {
10265         var rows = this.el.select('tbody > tr', true).elements;
10266         
10267         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10268         
10269     },
10270     getCellDom : function(rowIndex, colIndex)
10271     {
10272         var row = this.getRowDom(rowIndex);
10273         if (row === false) {
10274             return false;
10275         }
10276         var cols = row.select('td', true).elements;
10277         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10278         
10279     },
10280     
10281     // returns the object tree for a tr..
10282   
10283     
10284     renderRow : function(cm, ds, rowIndex) 
10285     {
10286         var d = ds.getAt(rowIndex);
10287         
10288         var row = {
10289             tag : 'tr',
10290             cls : 'x-row-' + rowIndex,
10291             cn : []
10292         };
10293             
10294         var cellObjects = [];
10295         
10296         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10297             var config = cm.config[i];
10298             
10299             var renderer = cm.getRenderer(i);
10300             var value = '';
10301             var id = false;
10302             
10303             if(typeof(renderer) !== 'undefined'){
10304                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10305             }
10306             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10307             // and are rendered into the cells after the row is rendered - using the id for the element.
10308             
10309             if(typeof(value) === 'object'){
10310                 id = Roo.id();
10311                 cellObjects.push({
10312                     container : id,
10313                     cfg : value 
10314                 })
10315             }
10316             
10317             var rowcfg = {
10318                 record: d,
10319                 rowIndex : rowIndex,
10320                 colIndex : i,
10321                 rowClass : ''
10322             };
10323
10324             this.fireEvent('rowclass', this, rowcfg);
10325             
10326             var td = {
10327                 tag: 'td',
10328                 // this might end up displaying HTML?
10329                 // this is too messy... - better to only do it on columsn you know are going to be too long
10330                 //tooltip : (typeof(value) === 'object') ? '' : value,
10331                 cls : rowcfg.rowClass + ' x-col-' + i,
10332                 style: '',
10333                 html: (typeof(value) === 'object') ? '' : value
10334             };
10335             
10336             if (id) {
10337                 td.id = id;
10338             }
10339             
10340             if(typeof(config.colspan) != 'undefined'){
10341                 td.colspan = config.colspan;
10342             }
10343             
10344             
10345             
10346             if(typeof(config.align) != 'undefined' && config.align.length){
10347                 td.style += ' text-align:' + config.align + ';';
10348             }
10349             if(typeof(config.valign) != 'undefined' && config.valign.length){
10350                 td.style += ' vertical-align:' + config.valign + ';';
10351             }
10352             /*
10353             if(typeof(config.width) != 'undefined'){
10354                 td.style += ' width:' +  config.width + 'px;';
10355             }
10356             */
10357             
10358             if(typeof(config.cursor) != 'undefined'){
10359                 td.style += ' cursor:' +  config.cursor + ';';
10360             }
10361             
10362             if(typeof(config.cls) != 'undefined'){
10363                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10364             }
10365             if (this.responsive) {
10366                 ['xs','sm','md','lg'].map(function(size){
10367                     
10368                     if(typeof(config[size]) == 'undefined'){
10369                         return;
10370                     }
10371                     
10372                     
10373                       
10374                     if (!config[size]) { // 0 = hidden
10375                         // BS 4 '0' is treated as hide that column and below.
10376                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10377                         return;
10378                     }
10379                     
10380                     td.cls += ' col-' + size + '-' + config[size] + (
10381                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10382                     );
10383                      
10384     
10385                 });
10386             }
10387             row.cn.push(td);
10388            
10389         }
10390         
10391         row.cellObjects = cellObjects;
10392         
10393         return row;
10394           
10395     },
10396     
10397     
10398     
10399     onBeforeLoad : function()
10400     {
10401         this.el.unmask(); // if needed.
10402     },
10403      /**
10404      * Remove all rows
10405      */
10406     clear : function()
10407     {
10408         this.el.select('tbody', true).first().dom.innerHTML = '';
10409     },
10410     /**
10411      * Show or hide a row.
10412      * @param {Number} rowIndex to show or hide
10413      * @param {Boolean} state hide
10414      */
10415     setRowVisibility : function(rowIndex, state)
10416     {
10417         var bt = this.bodyEl.dom;
10418         
10419         var rows = this.el.select('tbody > tr', true).elements;
10420         
10421         if(typeof(rows[rowIndex]) == 'undefined'){
10422             return;
10423         }
10424         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10425         
10426     },
10427     
10428     
10429     getSelectionModel : function(){
10430         if(!this.selModel){
10431             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10432         }
10433         return this.selModel;
10434     },
10435     /*
10436      * Render the Roo.bootstrap object from renderder
10437      */
10438     renderCellObject : function(r)
10439     {
10440         var _this = this;
10441         
10442         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10443         
10444         var t = r.cfg.render(r.container);
10445         
10446         if(r.cfg.cn){
10447             Roo.each(r.cfg.cn, function(c){
10448                 var child = {
10449                     container: t.getChildContainer(),
10450                     cfg: c
10451                 };
10452                 _this.renderCellObject(child);
10453             })
10454         }
10455     },
10456     /**
10457      * get the Row Index from a dom element.
10458      * @param {Roo.Element} row The row to look for
10459      * @returns {Number} the row
10460      */
10461     getRowIndex : function(row)
10462     {
10463         var rowIndex = -1;
10464         
10465         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10466             if(el != row){
10467                 return;
10468             }
10469             
10470             rowIndex = index;
10471         });
10472         
10473         return rowIndex;
10474     },
10475     /**
10476      * get the header TH element for columnIndex
10477      * @param {Number} columnIndex
10478      * @returns {Roo.Element}
10479      */
10480     getHeaderIndex: function(colIndex)
10481     {
10482         var cols = this.headEl.select('th', true).elements;
10483         return cols[colIndex]; 
10484     },
10485     /**
10486      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10487      * @param {domElement} cell to look for
10488      * @returns {Number} the column
10489      */
10490     getCellIndex : function(cell)
10491     {
10492         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10493         if(id){
10494             return parseInt(id[1], 10);
10495         }
10496         return 0;
10497     },
10498      /**
10499      * Returns the grid's underlying element = used by panel.Grid
10500      * @return {Element} The element
10501      */
10502     getGridEl : function(){
10503         return this.el;
10504     },
10505      /**
10506      * Forces a resize - used by panel.Grid
10507      * @return {Element} The element
10508      */
10509     autoSize : function()
10510     {
10511         //var ctr = Roo.get(this.container.dom.parentElement);
10512         var ctr = Roo.get(this.el.dom);
10513         
10514         var thd = this.getGridEl().select('thead',true).first();
10515         var tbd = this.getGridEl().select('tbody', true).first();
10516         var tfd = this.getGridEl().select('tfoot', true).first();
10517         
10518         var cw = ctr.getWidth();
10519         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10520         
10521         if (tbd) {
10522             
10523             tbd.setWidth(ctr.getWidth());
10524             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10525             // this needs fixing for various usage - currently only hydra job advers I think..
10526             //tdb.setHeight(
10527             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10528             //); 
10529             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10530             cw -= barsize;
10531         }
10532         cw = Math.max(cw, this.totalWidth);
10533         this.getGridEl().select('tbody tr',true).setWidth(cw);
10534         this.initCSS();
10535         
10536         // resize 'expandable coloumn?
10537         
10538         return; // we doe not have a view in this design..
10539         
10540     },
10541     onBodyScroll: function()
10542     {
10543         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10544         if(this.headEl){
10545             this.headEl.setStyle({
10546                 'position' : 'relative',
10547                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10548             });
10549         }
10550         
10551         if(this.lazyLoad){
10552             
10553             var scrollHeight = this.bodyEl.dom.scrollHeight;
10554             
10555             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10556             
10557             var height = this.bodyEl.getHeight();
10558             
10559             if(scrollHeight - height == scrollTop) {
10560                 
10561                 var total = this.ds.getTotalCount();
10562                 
10563                 if(this.footer.cursor + this.footer.pageSize < total){
10564                     
10565                     this.footer.ds.load({
10566                         params : {
10567                             start : this.footer.cursor + this.footer.pageSize,
10568                             limit : this.footer.pageSize
10569                         },
10570                         add : true
10571                     });
10572                 }
10573             }
10574             
10575         }
10576     },
10577     onColumnSplitterMoved : function(i, diff)
10578     {
10579         this.userResized = true;
10580         
10581         var cm = this.colModel;
10582         
10583         var w = this.getHeaderIndex(i).getWidth() + diff;
10584         
10585         
10586         cm.setColumnWidth(i, w, true);
10587         this.initCSS();
10588         //var cid = cm.getColumnId(i); << not used in this version?
10589        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10590         
10591         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10592         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10593         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10594 */
10595         //this.updateSplitters();
10596         //this.layout(); << ??
10597         this.fireEvent("columnresize", i, w);
10598     },
10599     onHeaderChange : function()
10600     {
10601         var header = this.renderHeader();
10602         var table = this.el.select('table', true).first();
10603         
10604         this.headEl.remove();
10605         this.headEl = table.createChild(header, this.bodyEl, false);
10606         
10607         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10608             e.on('click', this.sort, this);
10609         }, this);
10610         
10611         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10612             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10613         }
10614         
10615     },
10616     
10617     onHiddenChange : function(colModel, colIndex, hidden)
10618     {
10619         /*
10620         this.cm.setHidden()
10621         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10622         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10623         
10624         this.CSS.updateRule(thSelector, "display", "");
10625         this.CSS.updateRule(tdSelector, "display", "");
10626         
10627         if(hidden){
10628             this.CSS.updateRule(thSelector, "display", "none");
10629             this.CSS.updateRule(tdSelector, "display", "none");
10630         }
10631         */
10632         // onload calls initCSS()
10633         this.onHeaderChange();
10634         this.onLoad();
10635     },
10636     
10637     setColumnWidth: function(col_index, width)
10638     {
10639         // width = "md-2 xs-2..."
10640         if(!this.colModel.config[col_index]) {
10641             return;
10642         }
10643         
10644         var w = width.split(" ");
10645         
10646         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10647         
10648         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10649         
10650         
10651         for(var j = 0; j < w.length; j++) {
10652             
10653             if(!w[j]) {
10654                 continue;
10655             }
10656             
10657             var size_cls = w[j].split("-");
10658             
10659             if(!Number.isInteger(size_cls[1] * 1)) {
10660                 continue;
10661             }
10662             
10663             if(!this.colModel.config[col_index][size_cls[0]]) {
10664                 continue;
10665             }
10666             
10667             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10668                 continue;
10669             }
10670             
10671             h_row[0].classList.replace(
10672                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10673                 "col-"+size_cls[0]+"-"+size_cls[1]
10674             );
10675             
10676             for(var i = 0; i < rows.length; i++) {
10677                 
10678                 var size_cls = w[j].split("-");
10679                 
10680                 if(!Number.isInteger(size_cls[1] * 1)) {
10681                     continue;
10682                 }
10683                 
10684                 if(!this.colModel.config[col_index][size_cls[0]]) {
10685                     continue;
10686                 }
10687                 
10688                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10689                     continue;
10690                 }
10691                 
10692                 rows[i].classList.replace(
10693                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10694                     "col-"+size_cls[0]+"-"+size_cls[1]
10695                 );
10696             }
10697             
10698             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10699         }
10700     }
10701 });
10702
10703 // currently only used to find the split on drag.. 
10704 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10705
10706 /**
10707  * @depricated
10708 */
10709 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10710 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10711 /*
10712  * - LGPL
10713  *
10714  * table cell
10715  * 
10716  */
10717
10718 /**
10719  * @class Roo.bootstrap.TableCell
10720  * @extends Roo.bootstrap.Component
10721  * @children Roo.bootstrap.Component
10722  * @parent Roo.bootstrap.TableRow
10723  * Bootstrap TableCell class
10724  * 
10725  * @cfg {String} html cell contain text
10726  * @cfg {String} cls cell class
10727  * @cfg {String} tag cell tag (td|th) default td
10728  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10729  * @cfg {String} align Aligns the content in a cell
10730  * @cfg {String} axis Categorizes cells
10731  * @cfg {String} bgcolor Specifies the background color of a cell
10732  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10733  * @cfg {Number} colspan Specifies the number of columns a cell should span
10734  * @cfg {String} headers Specifies one or more header cells a cell is related to
10735  * @cfg {Number} height Sets the height of a cell
10736  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10737  * @cfg {Number} rowspan Sets the number of rows a cell should span
10738  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10739  * @cfg {String} valign Vertical aligns the content in a cell
10740  * @cfg {Number} width Specifies the width of a cell
10741  * 
10742  * @constructor
10743  * Create a new TableCell
10744  * @param {Object} config The config object
10745  */
10746
10747 Roo.bootstrap.TableCell = function(config){
10748     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10749 };
10750
10751 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10752     
10753     html: false,
10754     cls: false,
10755     tag: false,
10756     abbr: false,
10757     align: false,
10758     axis: false,
10759     bgcolor: false,
10760     charoff: false,
10761     colspan: false,
10762     headers: false,
10763     height: false,
10764     nowrap: false,
10765     rowspan: false,
10766     scope: false,
10767     valign: false,
10768     width: false,
10769     
10770     
10771     getAutoCreate : function(){
10772         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10773         
10774         cfg = {
10775             tag: 'td'
10776         };
10777         
10778         if(this.tag){
10779             cfg.tag = this.tag;
10780         }
10781         
10782         if (this.html) {
10783             cfg.html=this.html
10784         }
10785         if (this.cls) {
10786             cfg.cls=this.cls
10787         }
10788         if (this.abbr) {
10789             cfg.abbr=this.abbr
10790         }
10791         if (this.align) {
10792             cfg.align=this.align
10793         }
10794         if (this.axis) {
10795             cfg.axis=this.axis
10796         }
10797         if (this.bgcolor) {
10798             cfg.bgcolor=this.bgcolor
10799         }
10800         if (this.charoff) {
10801             cfg.charoff=this.charoff
10802         }
10803         if (this.colspan) {
10804             cfg.colspan=this.colspan
10805         }
10806         if (this.headers) {
10807             cfg.headers=this.headers
10808         }
10809         if (this.height) {
10810             cfg.height=this.height
10811         }
10812         if (this.nowrap) {
10813             cfg.nowrap=this.nowrap
10814         }
10815         if (this.rowspan) {
10816             cfg.rowspan=this.rowspan
10817         }
10818         if (this.scope) {
10819             cfg.scope=this.scope
10820         }
10821         if (this.valign) {
10822             cfg.valign=this.valign
10823         }
10824         if (this.width) {
10825             cfg.width=this.width
10826         }
10827         
10828         
10829         return cfg;
10830     }
10831    
10832 });
10833
10834  
10835
10836  /*
10837  * - LGPL
10838  *
10839  * table row
10840  * 
10841  */
10842
10843 /**
10844  * @class Roo.bootstrap.TableRow
10845  * @extends Roo.bootstrap.Component
10846  * @children Roo.bootstrap.TableCell
10847  * @parent Roo.bootstrap.TableBody
10848  * Bootstrap TableRow class
10849  * @cfg {String} cls row class
10850  * @cfg {String} align Aligns the content in a table row
10851  * @cfg {String} bgcolor Specifies a background color for a table row
10852  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10853  * @cfg {String} valign Vertical aligns the content in a table row
10854  * 
10855  * @constructor
10856  * Create a new TableRow
10857  * @param {Object} config The config object
10858  */
10859
10860 Roo.bootstrap.TableRow = function(config){
10861     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10862 };
10863
10864 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10865     
10866     cls: false,
10867     align: false,
10868     bgcolor: false,
10869     charoff: false,
10870     valign: false,
10871     
10872     getAutoCreate : function(){
10873         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10874         
10875         cfg = {
10876             tag: 'tr'
10877         };
10878             
10879         if(this.cls){
10880             cfg.cls = this.cls;
10881         }
10882         if(this.align){
10883             cfg.align = this.align;
10884         }
10885         if(this.bgcolor){
10886             cfg.bgcolor = this.bgcolor;
10887         }
10888         if(this.charoff){
10889             cfg.charoff = this.charoff;
10890         }
10891         if(this.valign){
10892             cfg.valign = this.valign;
10893         }
10894         
10895         return cfg;
10896     }
10897    
10898 });
10899
10900  
10901
10902  /*
10903  * - LGPL
10904  *
10905  * table body
10906  * 
10907  */
10908
10909 /**
10910  * @class Roo.bootstrap.TableBody
10911  * @extends Roo.bootstrap.Component
10912  * @children Roo.bootstrap.TableRow
10913  * @parent Roo.bootstrap.Table
10914  * Bootstrap TableBody class
10915  * @cfg {String} cls element class
10916  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10917  * @cfg {String} align Aligns the content inside the element
10918  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10919  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10920  * 
10921  * @constructor
10922  * Create a new TableBody
10923  * @param {Object} config The config object
10924  */
10925
10926 Roo.bootstrap.TableBody = function(config){
10927     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10928 };
10929
10930 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10931     
10932     cls: false,
10933     tag: false,
10934     align: false,
10935     charoff: false,
10936     valign: false,
10937     
10938     getAutoCreate : function(){
10939         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10940         
10941         cfg = {
10942             tag: 'tbody'
10943         };
10944             
10945         if (this.cls) {
10946             cfg.cls=this.cls
10947         }
10948         if(this.tag){
10949             cfg.tag = this.tag;
10950         }
10951         
10952         if(this.align){
10953             cfg.align = this.align;
10954         }
10955         if(this.charoff){
10956             cfg.charoff = this.charoff;
10957         }
10958         if(this.valign){
10959             cfg.valign = this.valign;
10960         }
10961         
10962         return cfg;
10963     }
10964     
10965     
10966 //    initEvents : function()
10967 //    {
10968 //        
10969 //        if(!this.store){
10970 //            return;
10971 //        }
10972 //        
10973 //        this.store = Roo.factory(this.store, Roo.data);
10974 //        this.store.on('load', this.onLoad, this);
10975 //        
10976 //        this.store.load();
10977 //        
10978 //    },
10979 //    
10980 //    onLoad: function () 
10981 //    {   
10982 //        this.fireEvent('load', this);
10983 //    }
10984 //    
10985 //   
10986 });
10987
10988  
10989
10990  /*
10991  * Based on:
10992  * Ext JS Library 1.1.1
10993  * Copyright(c) 2006-2007, Ext JS, LLC.
10994  *
10995  * Originally Released Under LGPL - original licence link has changed is not relivant.
10996  *
10997  * Fork - LGPL
10998  * <script type="text/javascript">
10999  */
11000
11001 // as we use this in bootstrap.
11002 Roo.namespace('Roo.form');
11003  /**
11004  * @class Roo.form.Action
11005  * Internal Class used to handle form actions
11006  * @constructor
11007  * @param {Roo.form.BasicForm} el The form element or its id
11008  * @param {Object} config Configuration options
11009  */
11010
11011  
11012  
11013 // define the action interface
11014 Roo.form.Action = function(form, options){
11015     this.form = form;
11016     this.options = options || {};
11017 };
11018 /**
11019  * Client Validation Failed
11020  * @const 
11021  */
11022 Roo.form.Action.CLIENT_INVALID = 'client';
11023 /**
11024  * Server Validation Failed
11025  * @const 
11026  */
11027 Roo.form.Action.SERVER_INVALID = 'server';
11028  /**
11029  * Connect to Server Failed
11030  * @const 
11031  */
11032 Roo.form.Action.CONNECT_FAILURE = 'connect';
11033 /**
11034  * Reading Data from Server Failed
11035  * @const 
11036  */
11037 Roo.form.Action.LOAD_FAILURE = 'load';
11038
11039 Roo.form.Action.prototype = {
11040     type : 'default',
11041     failureType : undefined,
11042     response : undefined,
11043     result : undefined,
11044
11045     // interface method
11046     run : function(options){
11047
11048     },
11049
11050     // interface method
11051     success : function(response){
11052
11053     },
11054
11055     // interface method
11056     handleResponse : function(response){
11057
11058     },
11059
11060     // default connection failure
11061     failure : function(response){
11062         
11063         this.response = response;
11064         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11065         this.form.afterAction(this, false);
11066     },
11067
11068     processResponse : function(response){
11069         this.response = response;
11070         if(!response.responseText){
11071             return true;
11072         }
11073         this.result = this.handleResponse(response);
11074         return this.result;
11075     },
11076
11077     // utility functions used internally
11078     getUrl : function(appendParams){
11079         var url = this.options.url || this.form.url || this.form.el.dom.action;
11080         if(appendParams){
11081             var p = this.getParams();
11082             if(p){
11083                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11084             }
11085         }
11086         return url;
11087     },
11088
11089     getMethod : function(){
11090         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11091     },
11092
11093     getParams : function(){
11094         var bp = this.form.baseParams;
11095         var p = this.options.params;
11096         if(p){
11097             if(typeof p == "object"){
11098                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11099             }else if(typeof p == 'string' && bp){
11100                 p += '&' + Roo.urlEncode(bp);
11101             }
11102         }else if(bp){
11103             p = Roo.urlEncode(bp);
11104         }
11105         return p;
11106     },
11107
11108     createCallback : function(){
11109         return {
11110             success: this.success,
11111             failure: this.failure,
11112             scope: this,
11113             timeout: (this.form.timeout*1000),
11114             upload: this.form.fileUpload ? this.success : undefined
11115         };
11116     }
11117 };
11118
11119 Roo.form.Action.Submit = function(form, options){
11120     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11121 };
11122
11123 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11124     type : 'submit',
11125
11126     haveProgress : false,
11127     uploadComplete : false,
11128     
11129     // uploadProgress indicator.
11130     uploadProgress : function()
11131     {
11132         if (!this.form.progressUrl) {
11133             return;
11134         }
11135         
11136         if (!this.haveProgress) {
11137             Roo.MessageBox.progress("Uploading", "Uploading");
11138         }
11139         if (this.uploadComplete) {
11140            Roo.MessageBox.hide();
11141            return;
11142         }
11143         
11144         this.haveProgress = true;
11145    
11146         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11147         
11148         var c = new Roo.data.Connection();
11149         c.request({
11150             url : this.form.progressUrl,
11151             params: {
11152                 id : uid
11153             },
11154             method: 'GET',
11155             success : function(req){
11156                //console.log(data);
11157                 var rdata = false;
11158                 var edata;
11159                 try  {
11160                    rdata = Roo.decode(req.responseText)
11161                 } catch (e) {
11162                     Roo.log("Invalid data from server..");
11163                     Roo.log(edata);
11164                     return;
11165                 }
11166                 if (!rdata || !rdata.success) {
11167                     Roo.log(rdata);
11168                     Roo.MessageBox.alert(Roo.encode(rdata));
11169                     return;
11170                 }
11171                 var data = rdata.data;
11172                 
11173                 if (this.uploadComplete) {
11174                    Roo.MessageBox.hide();
11175                    return;
11176                 }
11177                    
11178                 if (data){
11179                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11180                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11181                     );
11182                 }
11183                 this.uploadProgress.defer(2000,this);
11184             },
11185        
11186             failure: function(data) {
11187                 Roo.log('progress url failed ');
11188                 Roo.log(data);
11189             },
11190             scope : this
11191         });
11192            
11193     },
11194     
11195     
11196     run : function()
11197     {
11198         // run get Values on the form, so it syncs any secondary forms.
11199         this.form.getValues();
11200         
11201         var o = this.options;
11202         var method = this.getMethod();
11203         var isPost = method == 'POST';
11204         if(o.clientValidation === false || this.form.isValid()){
11205             
11206             if (this.form.progressUrl) {
11207                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11208                     (new Date() * 1) + '' + Math.random());
11209                     
11210             } 
11211             
11212             
11213             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11214                 form:this.form.el.dom,
11215                 url:this.getUrl(!isPost),
11216                 method: method,
11217                 params:isPost ? this.getParams() : null,
11218                 isUpload: this.form.fileUpload,
11219                 formData : this.form.formData
11220             }));
11221             
11222             this.uploadProgress();
11223
11224         }else if (o.clientValidation !== false){ // client validation failed
11225             this.failureType = Roo.form.Action.CLIENT_INVALID;
11226             this.form.afterAction(this, false);
11227         }
11228     },
11229
11230     success : function(response)
11231     {
11232         this.uploadComplete= true;
11233         if (this.haveProgress) {
11234             Roo.MessageBox.hide();
11235         }
11236         
11237         
11238         var result = this.processResponse(response);
11239         if(result === true || result.success){
11240             this.form.afterAction(this, true);
11241             return;
11242         }
11243         if(result.errors){
11244             this.form.markInvalid(result.errors);
11245             this.failureType = Roo.form.Action.SERVER_INVALID;
11246         }
11247         this.form.afterAction(this, false);
11248     },
11249     failure : function(response)
11250     {
11251         this.uploadComplete= true;
11252         if (this.haveProgress) {
11253             Roo.MessageBox.hide();
11254         }
11255         
11256         this.response = response;
11257         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11258         this.form.afterAction(this, false);
11259     },
11260     
11261     handleResponse : function(response){
11262         if(this.form.errorReader){
11263             var rs = this.form.errorReader.read(response);
11264             var errors = [];
11265             if(rs.records){
11266                 for(var i = 0, len = rs.records.length; i < len; i++) {
11267                     var r = rs.records[i];
11268                     errors[i] = r.data;
11269                 }
11270             }
11271             if(errors.length < 1){
11272                 errors = null;
11273             }
11274             return {
11275                 success : rs.success,
11276                 errors : errors
11277             };
11278         }
11279         var ret = false;
11280         try {
11281             ret = Roo.decode(response.responseText);
11282         } catch (e) {
11283             ret = {
11284                 success: false,
11285                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11286                 errors : []
11287             };
11288         }
11289         return ret;
11290         
11291     }
11292 });
11293
11294
11295 Roo.form.Action.Load = function(form, options){
11296     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11297     this.reader = this.form.reader;
11298 };
11299
11300 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11301     type : 'load',
11302
11303     run : function(){
11304         
11305         Roo.Ajax.request(Roo.apply(
11306                 this.createCallback(), {
11307                     method:this.getMethod(),
11308                     url:this.getUrl(false),
11309                     params:this.getParams()
11310         }));
11311     },
11312
11313     success : function(response){
11314         
11315         var result = this.processResponse(response);
11316         if(result === true || !result.success || !result.data){
11317             this.failureType = Roo.form.Action.LOAD_FAILURE;
11318             this.form.afterAction(this, false);
11319             return;
11320         }
11321         this.form.clearInvalid();
11322         this.form.setValues(result.data);
11323         this.form.afterAction(this, true);
11324     },
11325
11326     handleResponse : function(response){
11327         if(this.form.reader){
11328             var rs = this.form.reader.read(response);
11329             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11330             return {
11331                 success : rs.success,
11332                 data : data
11333             };
11334         }
11335         return Roo.decode(response.responseText);
11336     }
11337 });
11338
11339 Roo.form.Action.ACTION_TYPES = {
11340     'load' : Roo.form.Action.Load,
11341     'submit' : Roo.form.Action.Submit
11342 };/*
11343  * - LGPL
11344  *
11345  * form
11346  *
11347  */
11348
11349 /**
11350  * @class Roo.bootstrap.form.Form
11351  * @extends Roo.bootstrap.Component
11352  * @children Roo.bootstrap.Component
11353  * Bootstrap Form class
11354  * @cfg {String} method  GET | POST (default POST)
11355  * @cfg {String} labelAlign top | left (default top)
11356  * @cfg {String} align left  | right - for navbars
11357  * @cfg {Boolean} loadMask load mask when submit (default true)
11358
11359  *
11360  * @constructor
11361  * Create a new Form
11362  * @param {Object} config The config object
11363  */
11364
11365
11366 Roo.bootstrap.form.Form = function(config){
11367     
11368     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11369     
11370     Roo.bootstrap.form.Form.popover.apply();
11371     
11372     this.addEvents({
11373         /**
11374          * @event clientvalidation
11375          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11376          * @param {Form} this
11377          * @param {Boolean} valid true if the form has passed client-side validation
11378          */
11379         clientvalidation: true,
11380         /**
11381          * @event beforeaction
11382          * Fires before any action is performed. Return false to cancel the action.
11383          * @param {Form} this
11384          * @param {Action} action The action to be performed
11385          */
11386         beforeaction: true,
11387         /**
11388          * @event actionfailed
11389          * Fires when an action fails.
11390          * @param {Form} this
11391          * @param {Action} action The action that failed
11392          */
11393         actionfailed : true,
11394         /**
11395          * @event actioncomplete
11396          * Fires when an action is completed.
11397          * @param {Form} this
11398          * @param {Action} action The action that completed
11399          */
11400         actioncomplete : true
11401     });
11402 };
11403
11404 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11405
11406      /**
11407      * @cfg {String} method
11408      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11409      */
11410     method : 'POST',
11411     /**
11412      * @cfg {String} url
11413      * The URL to use for form actions if one isn't supplied in the action options.
11414      */
11415     /**
11416      * @cfg {Boolean} fileUpload
11417      * Set to true if this form is a file upload.
11418      */
11419
11420     /**
11421      * @cfg {Object} baseParams
11422      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11423      */
11424
11425     /**
11426      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11427      */
11428     timeout: 30,
11429     /**
11430      * @cfg {Sting} align (left|right) for navbar forms
11431      */
11432     align : 'left',
11433
11434     // private
11435     activeAction : null,
11436
11437     /**
11438      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11439      * element by passing it or its id or mask the form itself by passing in true.
11440      * @type Mixed
11441      */
11442     waitMsgTarget : false,
11443
11444     loadMask : true,
11445     
11446     /**
11447      * @cfg {Boolean} errorMask (true|false) default false
11448      */
11449     errorMask : false,
11450     
11451     /**
11452      * @cfg {Number} maskOffset Default 100
11453      */
11454     maskOffset : 100,
11455     
11456     /**
11457      * @cfg {Boolean} maskBody
11458      */
11459     maskBody : false,
11460
11461     getAutoCreate : function(){
11462
11463         var cfg = {
11464             tag: 'form',
11465             method : this.method || 'POST',
11466             id : this.id || Roo.id(),
11467             cls : ''
11468         };
11469         if (this.parent().xtype.match(/^Nav/)) {
11470             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11471
11472         }
11473
11474         if (this.labelAlign == 'left' ) {
11475             cfg.cls += ' form-horizontal';
11476         }
11477
11478
11479         return cfg;
11480     },
11481     initEvents : function()
11482     {
11483         this.el.on('submit', this.onSubmit, this);
11484         // this was added as random key presses on the form where triggering form submit.
11485         this.el.on('keypress', function(e) {
11486             if (e.getCharCode() != 13) {
11487                 return true;
11488             }
11489             // we might need to allow it for textareas.. and some other items.
11490             // check e.getTarget().
11491
11492             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11493                 return true;
11494             }
11495
11496             Roo.log("keypress blocked");
11497
11498             e.preventDefault();
11499             return false;
11500         });
11501         
11502     },
11503     // private
11504     onSubmit : function(e){
11505         e.stopEvent();
11506     },
11507
11508      /**
11509      * Returns true if client-side validation on the form is successful.
11510      * @return Boolean
11511      */
11512     isValid : function(){
11513         var items = this.getItems();
11514         var valid = true;
11515         var target = false;
11516         
11517         items.each(function(f){
11518             
11519             if(f.validate()){
11520                 return;
11521             }
11522             
11523             Roo.log('invalid field: ' + f.name);
11524             
11525             valid = false;
11526
11527             if(!target && f.el.isVisible(true)){
11528                 target = f;
11529             }
11530            
11531         });
11532         
11533         if(this.errorMask && !valid){
11534             Roo.bootstrap.form.Form.popover.mask(this, target);
11535         }
11536         
11537         return valid;
11538     },
11539     
11540     /**
11541      * Returns true if any fields in this form have changed since their original load.
11542      * @return Boolean
11543      */
11544     isDirty : function(){
11545         var dirty = false;
11546         var items = this.getItems();
11547         items.each(function(f){
11548            if(f.isDirty()){
11549                dirty = true;
11550                return false;
11551            }
11552            return true;
11553         });
11554         return dirty;
11555     },
11556      /**
11557      * Performs a predefined action (submit or load) or custom actions you define on this form.
11558      * @param {String} actionName The name of the action type
11559      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11560      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11561      * accept other config options):
11562      * <pre>
11563 Property          Type             Description
11564 ----------------  ---------------  ----------------------------------------------------------------------------------
11565 url               String           The url for the action (defaults to the form's url)
11566 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11567 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11568 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11569                                    validate the form on the client (defaults to false)
11570      * </pre>
11571      * @return {BasicForm} this
11572      */
11573     doAction : function(action, options){
11574         if(typeof action == 'string'){
11575             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11576         }
11577         if(this.fireEvent('beforeaction', this, action) !== false){
11578             this.beforeAction(action);
11579             action.run.defer(100, action);
11580         }
11581         return this;
11582     },
11583
11584     // private
11585     beforeAction : function(action){
11586         var o = action.options;
11587         
11588         if(this.loadMask){
11589             
11590             if(this.maskBody){
11591                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11592             } else {
11593                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11594             }
11595         }
11596         // not really supported yet.. ??
11597
11598         //if(this.waitMsgTarget === true){
11599         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11600         //}else if(this.waitMsgTarget){
11601         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11602         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11603         //}else {
11604         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11605        // }
11606
11607     },
11608
11609     // private
11610     afterAction : function(action, success){
11611         this.activeAction = null;
11612         var o = action.options;
11613
11614         if(this.loadMask){
11615             
11616             if(this.maskBody){
11617                 Roo.get(document.body).unmask();
11618             } else {
11619                 this.el.unmask();
11620             }
11621         }
11622         
11623         //if(this.waitMsgTarget === true){
11624 //            this.el.unmask();
11625         //}else if(this.waitMsgTarget){
11626         //    this.waitMsgTarget.unmask();
11627         //}else{
11628         //    Roo.MessageBox.updateProgress(1);
11629         //    Roo.MessageBox.hide();
11630        // }
11631         //
11632         if(success){
11633             if(o.reset){
11634                 this.reset();
11635             }
11636             Roo.callback(o.success, o.scope, [this, action]);
11637             this.fireEvent('actioncomplete', this, action);
11638
11639         }else{
11640
11641             // failure condition..
11642             // we have a scenario where updates need confirming.
11643             // eg. if a locking scenario exists..
11644             // we look for { errors : { needs_confirm : true }} in the response.
11645             if (
11646                 (typeof(action.result) != 'undefined')  &&
11647                 (typeof(action.result.errors) != 'undefined')  &&
11648                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11649            ){
11650                 var _t = this;
11651                 Roo.log("not supported yet");
11652                  /*
11653
11654                 Roo.MessageBox.confirm(
11655                     "Change requires confirmation",
11656                     action.result.errorMsg,
11657                     function(r) {
11658                         if (r != 'yes') {
11659                             return;
11660                         }
11661                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11662                     }
11663
11664                 );
11665                 */
11666
11667
11668                 return;
11669             }
11670
11671             Roo.callback(o.failure, o.scope, [this, action]);
11672             // show an error message if no failed handler is set..
11673             if (!this.hasListener('actionfailed')) {
11674                 Roo.log("need to add dialog support");
11675                 /*
11676                 Roo.MessageBox.alert("Error",
11677                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11678                         action.result.errorMsg :
11679                         "Saving Failed, please check your entries or try again"
11680                 );
11681                 */
11682             }
11683
11684             this.fireEvent('actionfailed', this, action);
11685         }
11686
11687     },
11688     /**
11689      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11690      * @param {String} id The value to search for
11691      * @return Field
11692      */
11693     findField : function(id){
11694         var items = this.getItems();
11695         var field = items.get(id);
11696         if(!field){
11697              items.each(function(f){
11698                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11699                     field = f;
11700                     return false;
11701                 }
11702                 return true;
11703             });
11704         }
11705         return field || null;
11706     },
11707      /**
11708      * Mark fields in this form invalid in bulk.
11709      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11710      * @return {BasicForm} this
11711      */
11712     markInvalid : function(errors){
11713         if(errors instanceof Array){
11714             for(var i = 0, len = errors.length; i < len; i++){
11715                 var fieldError = errors[i];
11716                 var f = this.findField(fieldError.id);
11717                 if(f){
11718                     f.markInvalid(fieldError.msg);
11719                 }
11720             }
11721         }else{
11722             var field, id;
11723             for(id in errors){
11724                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11725                     field.markInvalid(errors[id]);
11726                 }
11727             }
11728         }
11729         //Roo.each(this.childForms || [], function (f) {
11730         //    f.markInvalid(errors);
11731         //});
11732
11733         return this;
11734     },
11735
11736     /**
11737      * Set values for fields in this form in bulk.
11738      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11739      * @return {BasicForm} this
11740      */
11741     setValues : function(values){
11742         if(values instanceof Array){ // array of objects
11743             for(var i = 0, len = values.length; i < len; i++){
11744                 var v = values[i];
11745                 var f = this.findField(v.id);
11746                 if(f){
11747                     f.setValue(v.value);
11748                     if(this.trackResetOnLoad){
11749                         f.originalValue = f.getValue();
11750                     }
11751                 }
11752             }
11753         }else{ // object hash
11754             var field, id;
11755             for(id in values){
11756                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11757
11758                     if (field.setFromData &&
11759                         field.valueField &&
11760                         field.displayField &&
11761                         // combos' with local stores can
11762                         // be queried via setValue()
11763                         // to set their value..
11764                         (field.store && !field.store.isLocal)
11765                         ) {
11766                         // it's a combo
11767                         var sd = { };
11768                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11769                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11770                         field.setFromData(sd);
11771
11772                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11773                         
11774                         field.setFromData(values);
11775                         
11776                     } else {
11777                         field.setValue(values[id]);
11778                     }
11779
11780
11781                     if(this.trackResetOnLoad){
11782                         field.originalValue = field.getValue();
11783                     }
11784                 }
11785             }
11786         }
11787
11788         //Roo.each(this.childForms || [], function (f) {
11789         //    f.setValues(values);
11790         //});
11791
11792         return this;
11793     },
11794
11795     /**
11796      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11797      * they are returned as an array.
11798      * @param {Boolean} asString
11799      * @return {Object}
11800      */
11801     getValues : function(asString){
11802         //if (this.childForms) {
11803             // copy values from the child forms
11804         //    Roo.each(this.childForms, function (f) {
11805         //        this.setValues(f.getValues());
11806         //    }, this);
11807         //}
11808
11809
11810
11811         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11812         if(asString === true){
11813             return fs;
11814         }
11815         return Roo.urlDecode(fs);
11816     },
11817
11818     /**
11819      * Returns the fields in this form as an object with key/value pairs.
11820      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11821      * @return {Object}
11822      */
11823     getFieldValues : function(with_hidden)
11824     {
11825         var items = this.getItems();
11826         var ret = {};
11827         items.each(function(f){
11828             
11829             if (!f.getName()) {
11830                 return;
11831             }
11832             
11833             var v = f.getValue();
11834             
11835             if (f.inputType =='radio') {
11836                 if (typeof(ret[f.getName()]) == 'undefined') {
11837                     ret[f.getName()] = ''; // empty..
11838                 }
11839
11840                 if (!f.el.dom.checked) {
11841                     return;
11842
11843                 }
11844                 v = f.el.dom.value;
11845
11846             }
11847             
11848             if(f.xtype == 'MoneyField'){
11849                 ret[f.currencyName] = f.getCurrency();
11850             }
11851
11852             // not sure if this supported any more..
11853             if ((typeof(v) == 'object') && f.getRawValue) {
11854                 v = f.getRawValue() ; // dates..
11855             }
11856             // combo boxes where name != hiddenName...
11857             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11858                 ret[f.name] = f.getRawValue();
11859             }
11860             ret[f.getName()] = v;
11861         });
11862
11863         return ret;
11864     },
11865
11866     /**
11867      * Clears all invalid messages in this form.
11868      * @return {BasicForm} this
11869      */
11870     clearInvalid : function(){
11871         var items = this.getItems();
11872
11873         items.each(function(f){
11874            f.clearInvalid();
11875         });
11876
11877         return this;
11878     },
11879
11880     /**
11881      * Resets this form.
11882      * @return {BasicForm} this
11883      */
11884     reset : function(){
11885         var items = this.getItems();
11886         items.each(function(f){
11887             f.reset();
11888         });
11889
11890         Roo.each(this.childForms || [], function (f) {
11891             f.reset();
11892         });
11893
11894
11895         return this;
11896     },
11897     
11898     getItems : function()
11899     {
11900         var r=new Roo.util.MixedCollection(false, function(o){
11901             return o.id || (o.id = Roo.id());
11902         });
11903         var iter = function(el) {
11904             if (el.inputEl) {
11905                 r.add(el);
11906             }
11907             if (!el.items) {
11908                 return;
11909             }
11910             Roo.each(el.items,function(e) {
11911                 iter(e);
11912             });
11913         };
11914
11915         iter(this);
11916         return r;
11917     },
11918     
11919     hideFields : function(items)
11920     {
11921         Roo.each(items, function(i){
11922             
11923             var f = this.findField(i);
11924             
11925             if(!f){
11926                 return;
11927             }
11928             
11929             f.hide();
11930             
11931         }, this);
11932     },
11933     
11934     showFields : function(items)
11935     {
11936         Roo.each(items, function(i){
11937             
11938             var f = this.findField(i);
11939             
11940             if(!f){
11941                 return;
11942             }
11943             
11944             f.show();
11945             
11946         }, this);
11947     }
11948
11949 });
11950
11951 Roo.apply(Roo.bootstrap.form.Form, {
11952     
11953     popover : {
11954         
11955         padding : 5,
11956         
11957         isApplied : false,
11958         
11959         isMasked : false,
11960         
11961         form : false,
11962         
11963         target : false,
11964         
11965         toolTip : false,
11966         
11967         intervalID : false,
11968         
11969         maskEl : false,
11970         
11971         apply : function()
11972         {
11973             if(this.isApplied){
11974                 return;
11975             }
11976             
11977             this.maskEl = {
11978                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11979                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11980                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11981                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11982             };
11983             
11984             this.maskEl.top.enableDisplayMode("block");
11985             this.maskEl.left.enableDisplayMode("block");
11986             this.maskEl.bottom.enableDisplayMode("block");
11987             this.maskEl.right.enableDisplayMode("block");
11988             
11989             this.toolTip = new Roo.bootstrap.Tooltip({
11990                 cls : 'roo-form-error-popover',
11991                 alignment : {
11992                     'left' : ['r-l', [-2,0], 'right'],
11993                     'right' : ['l-r', [2,0], 'left'],
11994                     'bottom' : ['tl-bl', [0,2], 'top'],
11995                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11996                 }
11997             });
11998             
11999             this.toolTip.render(Roo.get(document.body));
12000
12001             this.toolTip.el.enableDisplayMode("block");
12002             
12003             Roo.get(document.body).on('click', function(){
12004                 this.unmask();
12005             }, this);
12006             
12007             Roo.get(document.body).on('touchstart', function(){
12008                 this.unmask();
12009             }, this);
12010             
12011             this.isApplied = true
12012         },
12013         
12014         mask : function(form, target)
12015         {
12016             this.form = form;
12017             
12018             this.target = target;
12019             
12020             if(!this.form.errorMask || !target.el){
12021                 return;
12022             }
12023             
12024             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12025             
12026             Roo.log(scrollable);
12027             
12028             var ot = this.target.el.calcOffsetsTo(scrollable);
12029             
12030             var scrollTo = ot[1] - this.form.maskOffset;
12031             
12032             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12033             
12034             scrollable.scrollTo('top', scrollTo);
12035             
12036             var box = this.target.el.getBox();
12037             Roo.log(box);
12038             var zIndex = Roo.bootstrap.Modal.zIndex++;
12039
12040             
12041             this.maskEl.top.setStyle('position', 'absolute');
12042             this.maskEl.top.setStyle('z-index', zIndex);
12043             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12044             this.maskEl.top.setLeft(0);
12045             this.maskEl.top.setTop(0);
12046             this.maskEl.top.show();
12047             
12048             this.maskEl.left.setStyle('position', 'absolute');
12049             this.maskEl.left.setStyle('z-index', zIndex);
12050             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12051             this.maskEl.left.setLeft(0);
12052             this.maskEl.left.setTop(box.y - this.padding);
12053             this.maskEl.left.show();
12054
12055             this.maskEl.bottom.setStyle('position', 'absolute');
12056             this.maskEl.bottom.setStyle('z-index', zIndex);
12057             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12058             this.maskEl.bottom.setLeft(0);
12059             this.maskEl.bottom.setTop(box.bottom + this.padding);
12060             this.maskEl.bottom.show();
12061
12062             this.maskEl.right.setStyle('position', 'absolute');
12063             this.maskEl.right.setStyle('z-index', zIndex);
12064             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12065             this.maskEl.right.setLeft(box.right + this.padding);
12066             this.maskEl.right.setTop(box.y - this.padding);
12067             this.maskEl.right.show();
12068
12069             this.toolTip.bindEl = this.target.el;
12070
12071             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12072
12073             var tip = this.target.blankText;
12074
12075             if(this.target.getValue() !== '' ) {
12076                 
12077                 if (this.target.invalidText.length) {
12078                     tip = this.target.invalidText;
12079                 } else if (this.target.regexText.length){
12080                     tip = this.target.regexText;
12081                 }
12082             }
12083
12084             this.toolTip.show(tip);
12085
12086             this.intervalID = window.setInterval(function() {
12087                 Roo.bootstrap.form.Form.popover.unmask();
12088             }, 10000);
12089
12090             window.onwheel = function(){ return false;};
12091             
12092             (function(){ this.isMasked = true; }).defer(500, this);
12093             
12094         },
12095         
12096         unmask : function()
12097         {
12098             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12099                 return;
12100             }
12101             
12102             this.maskEl.top.setStyle('position', 'absolute');
12103             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12104             this.maskEl.top.hide();
12105
12106             this.maskEl.left.setStyle('position', 'absolute');
12107             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12108             this.maskEl.left.hide();
12109
12110             this.maskEl.bottom.setStyle('position', 'absolute');
12111             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12112             this.maskEl.bottom.hide();
12113
12114             this.maskEl.right.setStyle('position', 'absolute');
12115             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12116             this.maskEl.right.hide();
12117             
12118             this.toolTip.hide();
12119             
12120             this.toolTip.el.hide();
12121             
12122             window.onwheel = function(){ return true;};
12123             
12124             if(this.intervalID){
12125                 window.clearInterval(this.intervalID);
12126                 this.intervalID = false;
12127             }
12128             
12129             this.isMasked = false;
12130             
12131         }
12132         
12133     }
12134     
12135 });
12136
12137 /*
12138  * Based on:
12139  * Ext JS Library 1.1.1
12140  * Copyright(c) 2006-2007, Ext JS, LLC.
12141  *
12142  * Originally Released Under LGPL - original licence link has changed is not relivant.
12143  *
12144  * Fork - LGPL
12145  * <script type="text/javascript">
12146  */
12147 /**
12148  * @class Roo.form.VTypes
12149  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12150  * @static
12151  */
12152 Roo.form.VTypes = function(){
12153     // closure these in so they are only created once.
12154     var alpha = /^[a-zA-Z_]+$/;
12155     var alphanum = /^[a-zA-Z0-9_]+$/;
12156     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12157     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12158
12159     // All these messages and functions are configurable
12160     return {
12161         /**
12162          * The function used to validate email addresses
12163          * @param {String} value The email address
12164          */
12165         'email' : function(v){
12166             return email.test(v);
12167         },
12168         /**
12169          * The error text to display when the email validation function returns false
12170          * @type String
12171          */
12172         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12173         /**
12174          * The keystroke filter mask to be applied on email input
12175          * @type RegExp
12176          */
12177         'emailMask' : /[a-z0-9_\.\-@]/i,
12178
12179         /**
12180          * The function used to validate URLs
12181          * @param {String} value The URL
12182          */
12183         'url' : function(v){
12184             return url.test(v);
12185         },
12186         /**
12187          * The error text to display when the url validation function returns false
12188          * @type String
12189          */
12190         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12191         
12192         /**
12193          * The function used to validate alpha values
12194          * @param {String} value The value
12195          */
12196         'alpha' : function(v){
12197             return alpha.test(v);
12198         },
12199         /**
12200          * The error text to display when the alpha validation function returns false
12201          * @type String
12202          */
12203         'alphaText' : 'This field should only contain letters and _',
12204         /**
12205          * The keystroke filter mask to be applied on alpha input
12206          * @type RegExp
12207          */
12208         'alphaMask' : /[a-z_]/i,
12209
12210         /**
12211          * The function used to validate alphanumeric values
12212          * @param {String} value The value
12213          */
12214         'alphanum' : function(v){
12215             return alphanum.test(v);
12216         },
12217         /**
12218          * The error text to display when the alphanumeric validation function returns false
12219          * @type String
12220          */
12221         'alphanumText' : 'This field should only contain letters, numbers and _',
12222         /**
12223          * The keystroke filter mask to be applied on alphanumeric input
12224          * @type RegExp
12225          */
12226         'alphanumMask' : /[a-z0-9_]/i
12227     };
12228 }();/*
12229  * - LGPL
12230  *
12231  * Input
12232  * 
12233  */
12234
12235 /**
12236  * @class Roo.bootstrap.form.Input
12237  * @extends Roo.bootstrap.Component
12238  * Bootstrap Input class
12239  * @cfg {Boolean} disabled is it disabled
12240  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12241  * @cfg {String} name name of the input
12242  * @cfg {string} fieldLabel - the label associated
12243  * @cfg {string} placeholder - placeholder to put in text.
12244  * @cfg {string} before - input group add on before
12245  * @cfg {string} after - input group add on after
12246  * @cfg {string} size - (lg|sm) or leave empty..
12247  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12248  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12249  * @cfg {Number} md colspan out of 12 for computer-sized screens
12250  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12251  * @cfg {string} value default value of the input
12252  * @cfg {Number} labelWidth set the width of label 
12253  * @cfg {Number} labellg set the width of label (1-12)
12254  * @cfg {Number} labelmd set the width of label (1-12)
12255  * @cfg {Number} labelsm set the width of label (1-12)
12256  * @cfg {Number} labelxs set the width of label (1-12)
12257  * @cfg {String} labelAlign (top|left)
12258  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12259  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12260  * @cfg {String} indicatorpos (left|right) default left
12261  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12262  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12263  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12264  * @cfg {Roo.bootstrap.Button} before Button to show before
12265  * @cfg {Roo.bootstrap.Button} afterButton to show before
12266  * @cfg {String} align (left|center|right) Default left
12267  * @cfg {Boolean} forceFeedback (true|false) Default false
12268  * 
12269  * @constructor
12270  * Create a new Input
12271  * @param {Object} config The config object
12272  */
12273
12274 Roo.bootstrap.form.Input = function(config){
12275     
12276     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12277     
12278     this.addEvents({
12279         /**
12280          * @event focus
12281          * Fires when this field receives input focus.
12282          * @param {Roo.form.Field} this
12283          */
12284         focus : true,
12285         /**
12286          * @event blur
12287          * Fires when this field loses input focus.
12288          * @param {Roo.form.Field} this
12289          */
12290         blur : true,
12291         /**
12292          * @event specialkey
12293          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12294          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12295          * @param {Roo.form.Field} this
12296          * @param {Roo.EventObject} e The event object
12297          */
12298         specialkey : true,
12299         /**
12300          * @event change
12301          * Fires just before the field blurs if the field value has changed.
12302          * @param {Roo.form.Field} this
12303          * @param {Mixed} newValue The new value
12304          * @param {Mixed} oldValue The original value
12305          */
12306         change : true,
12307         /**
12308          * @event invalid
12309          * Fires after the field has been marked as invalid.
12310          * @param {Roo.form.Field} this
12311          * @param {String} msg The validation message
12312          */
12313         invalid : true,
12314         /**
12315          * @event valid
12316          * Fires after the field has been validated with no errors.
12317          * @param {Roo.form.Field} this
12318          */
12319         valid : true,
12320          /**
12321          * @event keyup
12322          * Fires after the key up
12323          * @param {Roo.form.Field} this
12324          * @param {Roo.EventObject}  e The event Object
12325          */
12326         keyup : true,
12327         /**
12328          * @event paste
12329          * Fires after the user pastes into input
12330          * @param {Roo.form.Field} this
12331          * @param {Roo.EventObject}  e The event Object
12332          */
12333         paste : true
12334     });
12335 };
12336
12337 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12338      /**
12339      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12340       automatic validation (defaults to "keyup").
12341      */
12342     validationEvent : "keyup",
12343      /**
12344      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12345      */
12346     validateOnBlur : true,
12347     /**
12348      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12349      */
12350     validationDelay : 250,
12351      /**
12352      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12353      */
12354     focusClass : "x-form-focus",  // not needed???
12355     
12356        
12357     /**
12358      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12359      */
12360     invalidClass : "has-warning",
12361     
12362     /**
12363      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12364      */
12365     validClass : "has-success",
12366     
12367     /**
12368      * @cfg {Boolean} hasFeedback (true|false) default true
12369      */
12370     hasFeedback : true,
12371     
12372     /**
12373      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12374      */
12375     invalidFeedbackClass : "glyphicon-warning-sign",
12376     
12377     /**
12378      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12379      */
12380     validFeedbackClass : "glyphicon-ok",
12381     
12382     /**
12383      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12384      */
12385     selectOnFocus : false,
12386     
12387      /**
12388      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12389      */
12390     maskRe : null,
12391        /**
12392      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12393      */
12394     vtype : null,
12395     
12396       /**
12397      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12398      */
12399     disableKeyFilter : false,
12400     
12401        /**
12402      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12403      */
12404     disabled : false,
12405      /**
12406      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12407      */
12408     allowBlank : true,
12409     /**
12410      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12411      */
12412     blankText : "Please complete this mandatory field",
12413     
12414      /**
12415      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12416      */
12417     minLength : 0,
12418     /**
12419      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12420      */
12421     maxLength : Number.MAX_VALUE,
12422     /**
12423      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12424      */
12425     minLengthText : "The minimum length for this field is {0}",
12426     /**
12427      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12428      */
12429     maxLengthText : "The maximum length for this field is {0}",
12430   
12431     
12432     /**
12433      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12434      * If available, this function will be called only after the basic validators all return true, and will be passed the
12435      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12436      */
12437     validator : null,
12438     /**
12439      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12440      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12441      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12442      */
12443     regex : null,
12444     /**
12445      * @cfg {String} regexText -- Depricated - use Invalid Text
12446      */
12447     regexText : "",
12448     
12449     /**
12450      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12451      */
12452     invalidText : "",
12453     
12454     
12455     
12456     autocomplete: false,
12457     
12458     
12459     fieldLabel : '',
12460     inputType : 'text',
12461     
12462     name : false,
12463     placeholder: false,
12464     before : false,
12465     after : false,
12466     size : false,
12467     hasFocus : false,
12468     preventMark: false,
12469     isFormField : true,
12470     value : '',
12471     labelWidth : 2,
12472     labelAlign : false,
12473     readOnly : false,
12474     align : false,
12475     formatedValue : false,
12476     forceFeedback : false,
12477     
12478     indicatorpos : 'left',
12479     
12480     labellg : 0,
12481     labelmd : 0,
12482     labelsm : 0,
12483     labelxs : 0,
12484     
12485     capture : '',
12486     accept : '',
12487     
12488     parentLabelAlign : function()
12489     {
12490         var parent = this;
12491         while (parent.parent()) {
12492             parent = parent.parent();
12493             if (typeof(parent.labelAlign) !='undefined') {
12494                 return parent.labelAlign;
12495             }
12496         }
12497         return 'left';
12498         
12499     },
12500     
12501     getAutoCreate : function()
12502     {
12503         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12504         
12505         var id = Roo.id();
12506         
12507         var cfg = {};
12508         
12509         if(this.inputType != 'hidden'){
12510             cfg.cls = 'form-group' //input-group
12511         }
12512         
12513         var input =  {
12514             tag: 'input',
12515             id : id,
12516             type : this.inputType,
12517             value : this.value,
12518             cls : 'form-control',
12519             placeholder : this.placeholder || '',
12520             autocomplete : this.autocomplete || 'new-password'
12521         };
12522         if (this.inputType == 'file') {
12523             input.style = 'overflow:hidden'; // why not in CSS?
12524         }
12525         
12526         if(this.capture.length){
12527             input.capture = this.capture;
12528         }
12529         
12530         if(this.accept.length){
12531             input.accept = this.accept + "/*";
12532         }
12533         
12534         if(this.align){
12535             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12536         }
12537         
12538         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12539             input.maxLength = this.maxLength;
12540         }
12541         
12542         if (this.disabled) {
12543             input.disabled=true;
12544         }
12545         
12546         if (this.readOnly) {
12547             input.readonly=true;
12548         }
12549         
12550         if (this.name) {
12551             input.name = this.name;
12552         }
12553         
12554         if (this.size) {
12555             input.cls += ' input-' + this.size;
12556         }
12557         
12558         var settings=this;
12559         ['xs','sm','md','lg'].map(function(size){
12560             if (settings[size]) {
12561                 cfg.cls += ' col-' + size + '-' + settings[size];
12562             }
12563         });
12564         
12565         var inputblock = input;
12566         
12567         var feedback = {
12568             tag: 'span',
12569             cls: 'glyphicon form-control-feedback'
12570         };
12571             
12572         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12573             
12574             inputblock = {
12575                 cls : 'has-feedback',
12576                 cn :  [
12577                     input,
12578                     feedback
12579                 ] 
12580             };  
12581         }
12582         
12583         if (this.before || this.after) {
12584             
12585             inputblock = {
12586                 cls : 'input-group',
12587                 cn :  [] 
12588             };
12589             
12590             if (this.before && typeof(this.before) == 'string') {
12591                 
12592                 inputblock.cn.push({
12593                     tag :'span',
12594                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12595                     html : this.before
12596                 });
12597             }
12598             if (this.before && typeof(this.before) == 'object') {
12599                 this.before = Roo.factory(this.before);
12600                 
12601                 inputblock.cn.push({
12602                     tag :'span',
12603                     cls : 'roo-input-before input-group-prepend   input-group-' +
12604                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12605                 });
12606             }
12607             
12608             inputblock.cn.push(input);
12609             
12610             if (this.after && typeof(this.after) == 'string') {
12611                 inputblock.cn.push({
12612                     tag :'span',
12613                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12614                     html : this.after
12615                 });
12616             }
12617             if (this.after && typeof(this.after) == 'object') {
12618                 this.after = Roo.factory(this.after);
12619                 
12620                 inputblock.cn.push({
12621                     tag :'span',
12622                     cls : 'roo-input-after input-group-append  input-group-' +
12623                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12624                 });
12625             }
12626             
12627             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12628                 inputblock.cls += ' has-feedback';
12629                 inputblock.cn.push(feedback);
12630             }
12631         };
12632         var indicator = {
12633             tag : 'i',
12634             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12635             tooltip : 'This field is required'
12636         };
12637         if (this.allowBlank ) {
12638             indicator.style = this.allowBlank ? ' display:none' : '';
12639         }
12640         if (align ==='left' && this.fieldLabel.length) {
12641             
12642             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12643             
12644             cfg.cn = [
12645                 indicator,
12646                 {
12647                     tag: 'label',
12648                     'for' :  id,
12649                     cls : 'control-label col-form-label',
12650                     html : this.fieldLabel
12651
12652                 },
12653                 {
12654                     cls : "", 
12655                     cn: [
12656                         inputblock
12657                     ]
12658                 }
12659             ];
12660             
12661             var labelCfg = cfg.cn[1];
12662             var contentCfg = cfg.cn[2];
12663             
12664             if(this.indicatorpos == 'right'){
12665                 cfg.cn = [
12666                     {
12667                         tag: 'label',
12668                         'for' :  id,
12669                         cls : 'control-label col-form-label',
12670                         cn : [
12671                             {
12672                                 tag : 'span',
12673                                 html : this.fieldLabel
12674                             },
12675                             indicator
12676                         ]
12677                     },
12678                     {
12679                         cls : "",
12680                         cn: [
12681                             inputblock
12682                         ]
12683                     }
12684
12685                 ];
12686                 
12687                 labelCfg = cfg.cn[0];
12688                 contentCfg = cfg.cn[1];
12689             
12690             }
12691             
12692             if(this.labelWidth > 12){
12693                 labelCfg.style = "width: " + this.labelWidth + 'px';
12694             }
12695             
12696             if(this.labelWidth < 13 && this.labelmd == 0){
12697                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12698             }
12699             
12700             if(this.labellg > 0){
12701                 labelCfg.cls += ' col-lg-' + this.labellg;
12702                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12703             }
12704             
12705             if(this.labelmd > 0){
12706                 labelCfg.cls += ' col-md-' + this.labelmd;
12707                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12708             }
12709             
12710             if(this.labelsm > 0){
12711                 labelCfg.cls += ' col-sm-' + this.labelsm;
12712                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12713             }
12714             
12715             if(this.labelxs > 0){
12716                 labelCfg.cls += ' col-xs-' + this.labelxs;
12717                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12718             }
12719             
12720             
12721         } else if ( this.fieldLabel.length) {
12722                 
12723             
12724             
12725             cfg.cn = [
12726                 {
12727                     tag : 'i',
12728                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12729                     tooltip : 'This field is required',
12730                     style : this.allowBlank ? ' display:none' : '' 
12731                 },
12732                 {
12733                     tag: 'label',
12734                    //cls : 'input-group-addon',
12735                     html : this.fieldLabel
12736
12737                 },
12738
12739                inputblock
12740
12741            ];
12742            
12743            if(this.indicatorpos == 'right'){
12744        
12745                 cfg.cn = [
12746                     {
12747                         tag: 'label',
12748                        //cls : 'input-group-addon',
12749                         html : this.fieldLabel
12750
12751                     },
12752                     {
12753                         tag : 'i',
12754                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12755                         tooltip : 'This field is required',
12756                         style : this.allowBlank ? ' display:none' : '' 
12757                     },
12758
12759                    inputblock
12760
12761                ];
12762
12763             }
12764
12765         } else {
12766             
12767             cfg.cn = [
12768
12769                     inputblock
12770
12771             ];
12772                 
12773                 
12774         };
12775         
12776         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12777            cfg.cls += ' navbar-form';
12778         }
12779         
12780         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12781             // on BS4 we do this only if not form 
12782             cfg.cls += ' navbar-form';
12783             cfg.tag = 'li';
12784         }
12785         
12786         return cfg;
12787         
12788     },
12789     /**
12790      * return the real input element.
12791      */
12792     inputEl: function ()
12793     {
12794         return this.el.select('input.form-control',true).first();
12795     },
12796     
12797     tooltipEl : function()
12798     {
12799         return this.inputEl();
12800     },
12801     
12802     indicatorEl : function()
12803     {
12804         if (Roo.bootstrap.version == 4) {
12805             return false; // not enabled in v4 yet.
12806         }
12807         
12808         var indicator = this.el.select('i.roo-required-indicator',true).first();
12809         
12810         if(!indicator){
12811             return false;
12812         }
12813         
12814         return indicator;
12815         
12816     },
12817     
12818     setDisabled : function(v)
12819     {
12820         var i  = this.inputEl().dom;
12821         if (!v) {
12822             i.removeAttribute('disabled');
12823             return;
12824             
12825         }
12826         i.setAttribute('disabled','true');
12827     },
12828     initEvents : function()
12829     {
12830           
12831         this.inputEl().on("keydown" , this.fireKey,  this);
12832         this.inputEl().on("focus", this.onFocus,  this);
12833         this.inputEl().on("blur", this.onBlur,  this);
12834         
12835         this.inputEl().relayEvent('keyup', this);
12836         this.inputEl().relayEvent('paste', this);
12837         
12838         this.indicator = this.indicatorEl();
12839         
12840         if(this.indicator){
12841             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12842         }
12843  
12844         // reference to original value for reset
12845         this.originalValue = this.getValue();
12846         //Roo.form.TextField.superclass.initEvents.call(this);
12847         if(this.validationEvent == 'keyup'){
12848             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12849             this.inputEl().on('keyup', this.filterValidation, this);
12850         }
12851         else if(this.validationEvent !== false){
12852             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12853         }
12854         
12855         if(this.selectOnFocus){
12856             this.on("focus", this.preFocus, this);
12857             
12858         }
12859         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12860             this.inputEl().on("keypress", this.filterKeys, this);
12861         } else {
12862             this.inputEl().relayEvent('keypress', this);
12863         }
12864        /* if(this.grow){
12865             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12866             this.el.on("click", this.autoSize,  this);
12867         }
12868         */
12869         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12870             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12871         }
12872         
12873         if (typeof(this.before) == 'object') {
12874             this.before.render(this.el.select('.roo-input-before',true).first());
12875         }
12876         if (typeof(this.after) == 'object') {
12877             this.after.render(this.el.select('.roo-input-after',true).first());
12878         }
12879         
12880         this.inputEl().on('change', this.onChange, this);
12881         
12882     },
12883     filterValidation : function(e){
12884         if(!e.isNavKeyPress()){
12885             this.validationTask.delay(this.validationDelay);
12886         }
12887     },
12888      /**
12889      * Validates the field value
12890      * @return {Boolean} True if the value is valid, else false
12891      */
12892     validate : function(){
12893         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12894         if(this.disabled || this.validateValue(this.getRawValue())){
12895             this.markValid();
12896             return true;
12897         }
12898         
12899         this.markInvalid();
12900         return false;
12901     },
12902     
12903     
12904     /**
12905      * Validates a value according to the field's validation rules and marks the field as invalid
12906      * if the validation fails
12907      * @param {Mixed} value The value to validate
12908      * @return {Boolean} True if the value is valid, else false
12909      */
12910     validateValue : function(value)
12911     {
12912         if(this.getVisibilityEl().hasClass('hidden')){
12913             return true;
12914         }
12915         
12916         if(value.length < 1)  { // if it's blank
12917             if(this.allowBlank){
12918                 return true;
12919             }
12920             return false;
12921         }
12922         
12923         if(value.length < this.minLength){
12924             return false;
12925         }
12926         if(value.length > this.maxLength){
12927             return false;
12928         }
12929         if(this.vtype){
12930             var vt = Roo.form.VTypes;
12931             if(!vt[this.vtype](value, this)){
12932                 return false;
12933             }
12934         }
12935         if(typeof this.validator == "function"){
12936             var msg = this.validator(value);
12937             if(msg !== true){
12938                 return false;
12939             }
12940             if (typeof(msg) == 'string') {
12941                 this.invalidText = msg;
12942             }
12943         }
12944         
12945         if(this.regex && !this.regex.test(value)){
12946             return false;
12947         }
12948         
12949         return true;
12950     },
12951     
12952      // private
12953     fireKey : function(e){
12954         //Roo.log('field ' + e.getKey());
12955         if(e.isNavKeyPress()){
12956             this.fireEvent("specialkey", this, e);
12957         }
12958     },
12959     focus : function (selectText){
12960         if(this.rendered){
12961             this.inputEl().focus();
12962             if(selectText === true){
12963                 this.inputEl().dom.select();
12964             }
12965         }
12966         return this;
12967     } ,
12968     
12969     onFocus : function(){
12970         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12971            // this.el.addClass(this.focusClass);
12972         }
12973         if(!this.hasFocus){
12974             this.hasFocus = true;
12975             this.startValue = this.getValue();
12976             this.fireEvent("focus", this);
12977         }
12978     },
12979     
12980     beforeBlur : Roo.emptyFn,
12981
12982     
12983     // private
12984     onBlur : function(){
12985         this.beforeBlur();
12986         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12987             //this.el.removeClass(this.focusClass);
12988         }
12989         this.hasFocus = false;
12990         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12991             this.validate();
12992         }
12993         var v = this.getValue();
12994         if(String(v) !== String(this.startValue)){
12995             this.fireEvent('change', this, v, this.startValue);
12996         }
12997         this.fireEvent("blur", this);
12998     },
12999     
13000     onChange : function(e)
13001     {
13002         var v = this.getValue();
13003         if(String(v) !== String(this.startValue)){
13004             this.fireEvent('change', this, v, this.startValue);
13005         }
13006         
13007     },
13008     
13009     /**
13010      * Resets the current field value to the originally loaded value and clears any validation messages
13011      */
13012     reset : function(){
13013         this.setValue(this.originalValue);
13014         this.validate();
13015     },
13016      /**
13017      * Returns the name of the field
13018      * @return {Mixed} name The name field
13019      */
13020     getName: function(){
13021         return this.name;
13022     },
13023      /**
13024      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13025      * @return {Mixed} value The field value
13026      */
13027     getValue : function(){
13028         
13029         var v = this.inputEl().getValue();
13030         
13031         return v;
13032     },
13033     /**
13034      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13035      * @return {Mixed} value The field value
13036      */
13037     getRawValue : function(){
13038         var v = this.inputEl().getValue();
13039         
13040         return v;
13041     },
13042     
13043     /**
13044      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13045      * @param {Mixed} value The value to set
13046      */
13047     setRawValue : function(v){
13048         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13049     },
13050     
13051     selectText : function(start, end){
13052         var v = this.getRawValue();
13053         if(v.length > 0){
13054             start = start === undefined ? 0 : start;
13055             end = end === undefined ? v.length : end;
13056             var d = this.inputEl().dom;
13057             if(d.setSelectionRange){
13058                 d.setSelectionRange(start, end);
13059             }else if(d.createTextRange){
13060                 var range = d.createTextRange();
13061                 range.moveStart("character", start);
13062                 range.moveEnd("character", v.length-end);
13063                 range.select();
13064             }
13065         }
13066     },
13067     
13068     /**
13069      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13070      * @param {Mixed} value The value to set
13071      */
13072     setValue : function(v){
13073         this.value = v;
13074         if(this.rendered){
13075             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13076             this.validate();
13077         }
13078     },
13079     
13080     /*
13081     processValue : function(value){
13082         if(this.stripCharsRe){
13083             var newValue = value.replace(this.stripCharsRe, '');
13084             if(newValue !== value){
13085                 this.setRawValue(newValue);
13086                 return newValue;
13087             }
13088         }
13089         return value;
13090     },
13091   */
13092     preFocus : function(){
13093         
13094         if(this.selectOnFocus){
13095             this.inputEl().dom.select();
13096         }
13097     },
13098     filterKeys : function(e){
13099         var k = e.getKey();
13100         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13101             return;
13102         }
13103         var c = e.getCharCode(), cc = String.fromCharCode(c);
13104         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13105             return;
13106         }
13107         if(!this.maskRe.test(cc)){
13108             e.stopEvent();
13109         }
13110     },
13111      /**
13112      * Clear any invalid styles/messages for this field
13113      */
13114     clearInvalid : function(){
13115         
13116         if(!this.el || this.preventMark){ // not rendered
13117             return;
13118         }
13119         
13120         
13121         this.el.removeClass([this.invalidClass, 'is-invalid']);
13122         
13123         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13124             
13125             var feedback = this.el.select('.form-control-feedback', true).first();
13126             
13127             if(feedback){
13128                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13129             }
13130             
13131         }
13132         
13133         if(this.indicator){
13134             this.indicator.removeClass('visible');
13135             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13136         }
13137         
13138         this.fireEvent('valid', this);
13139     },
13140     
13141      /**
13142      * Mark this field as valid
13143      */
13144     markValid : function()
13145     {
13146         if(!this.el  || this.preventMark){ // not rendered...
13147             return;
13148         }
13149         
13150         this.el.removeClass([this.invalidClass, this.validClass]);
13151         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13152
13153         var feedback = this.el.select('.form-control-feedback', true).first();
13154             
13155         if(feedback){
13156             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13157         }
13158         
13159         if(this.indicator){
13160             this.indicator.removeClass('visible');
13161             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13162         }
13163         
13164         if(this.disabled){
13165             return;
13166         }
13167         
13168            
13169         if(this.allowBlank && !this.getRawValue().length){
13170             return;
13171         }
13172         if (Roo.bootstrap.version == 3) {
13173             this.el.addClass(this.validClass);
13174         } else {
13175             this.inputEl().addClass('is-valid');
13176         }
13177
13178         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13179             
13180             var feedback = this.el.select('.form-control-feedback', true).first();
13181             
13182             if(feedback){
13183                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13184                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13185             }
13186             
13187         }
13188         
13189         this.fireEvent('valid', this);
13190     },
13191     
13192      /**
13193      * Mark this field as invalid
13194      * @param {String} msg The validation message
13195      */
13196     markInvalid : function(msg)
13197     {
13198         if(!this.el  || this.preventMark){ // not rendered
13199             return;
13200         }
13201         
13202         this.el.removeClass([this.invalidClass, this.validClass]);
13203         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13204         
13205         var feedback = this.el.select('.form-control-feedback', true).first();
13206             
13207         if(feedback){
13208             this.el.select('.form-control-feedback', true).first().removeClass(
13209                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13210         }
13211
13212         if(this.disabled){
13213             return;
13214         }
13215         
13216         if(this.allowBlank && !this.getRawValue().length){
13217             return;
13218         }
13219         
13220         if(this.indicator){
13221             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13222             this.indicator.addClass('visible');
13223         }
13224         if (Roo.bootstrap.version == 3) {
13225             this.el.addClass(this.invalidClass);
13226         } else {
13227             this.inputEl().addClass('is-invalid');
13228         }
13229         
13230         
13231         
13232         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13233             
13234             var feedback = this.el.select('.form-control-feedback', true).first();
13235             
13236             if(feedback){
13237                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13238                 
13239                 if(this.getValue().length || this.forceFeedback){
13240                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13241                 }
13242                 
13243             }
13244             
13245         }
13246         
13247         this.fireEvent('invalid', this, msg);
13248     },
13249     // private
13250     SafariOnKeyDown : function(event)
13251     {
13252         // this is a workaround for a password hang bug on chrome/ webkit.
13253         if (this.inputEl().dom.type != 'password') {
13254             return;
13255         }
13256         
13257         var isSelectAll = false;
13258         
13259         if(this.inputEl().dom.selectionEnd > 0){
13260             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13261         }
13262         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13263             event.preventDefault();
13264             this.setValue('');
13265             return;
13266         }
13267         
13268         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13269             
13270             event.preventDefault();
13271             // this is very hacky as keydown always get's upper case.
13272             //
13273             var cc = String.fromCharCode(event.getCharCode());
13274             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13275             
13276         }
13277     },
13278     adjustWidth : function(tag, w){
13279         tag = tag.toLowerCase();
13280         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13281             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13282                 if(tag == 'input'){
13283                     return w + 2;
13284                 }
13285                 if(tag == 'textarea'){
13286                     return w-2;
13287                 }
13288             }else if(Roo.isOpera){
13289                 if(tag == 'input'){
13290                     return w + 2;
13291                 }
13292                 if(tag == 'textarea'){
13293                     return w-2;
13294                 }
13295             }
13296         }
13297         return w;
13298     },
13299     
13300     setFieldLabel : function(v)
13301     {
13302         if(!this.rendered){
13303             return;
13304         }
13305         
13306         if(this.indicatorEl()){
13307             var ar = this.el.select('label > span',true);
13308             
13309             if (ar.elements.length) {
13310                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13311                 this.fieldLabel = v;
13312                 return;
13313             }
13314             
13315             var br = this.el.select('label',true);
13316             
13317             if(br.elements.length) {
13318                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13319                 this.fieldLabel = v;
13320                 return;
13321             }
13322             
13323             Roo.log('Cannot Found any of label > span || label in input');
13324             return;
13325         }
13326         
13327         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13328         this.fieldLabel = v;
13329         
13330         
13331     }
13332 });
13333
13334  
13335 /*
13336  * - LGPL
13337  *
13338  * Input
13339  * 
13340  */
13341
13342 /**
13343  * @class Roo.bootstrap.form.TextArea
13344  * @extends Roo.bootstrap.form.Input
13345  * Bootstrap TextArea class
13346  * @cfg {Number} cols Specifies the visible width of a text area
13347  * @cfg {Number} rows Specifies the visible number of lines in a text area
13348  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13349  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13350  * @cfg {string} html text
13351  * 
13352  * @constructor
13353  * Create a new TextArea
13354  * @param {Object} config The config object
13355  */
13356
13357 Roo.bootstrap.form.TextArea = function(config){
13358     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13359    
13360 };
13361
13362 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13363      
13364     cols : false,
13365     rows : 5,
13366     readOnly : false,
13367     warp : 'soft',
13368     resize : false,
13369     value: false,
13370     html: false,
13371     
13372     getAutoCreate : function(){
13373         
13374         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13375         
13376         var id = Roo.id();
13377         
13378         var cfg = {};
13379         
13380         if(this.inputType != 'hidden'){
13381             cfg.cls = 'form-group' //input-group
13382         }
13383         
13384         var input =  {
13385             tag: 'textarea',
13386             id : id,
13387             warp : this.warp,
13388             rows : this.rows,
13389             value : this.value || '',
13390             html: this.html || '',
13391             cls : 'form-control',
13392             placeholder : this.placeholder || '' 
13393             
13394         };
13395         
13396         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13397             input.maxLength = this.maxLength;
13398         }
13399         
13400         if(this.resize){
13401             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13402         }
13403         
13404         if(this.cols){
13405             input.cols = this.cols;
13406         }
13407         
13408         if (this.readOnly) {
13409             input.readonly = true;
13410         }
13411         
13412         if (this.name) {
13413             input.name = this.name;
13414         }
13415         
13416         if (this.size) {
13417             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13418         }
13419         
13420         var settings=this;
13421         ['xs','sm','md','lg'].map(function(size){
13422             if (settings[size]) {
13423                 cfg.cls += ' col-' + size + '-' + settings[size];
13424             }
13425         });
13426         
13427         var inputblock = input;
13428         
13429         if(this.hasFeedback && !this.allowBlank){
13430             
13431             var feedback = {
13432                 tag: 'span',
13433                 cls: 'glyphicon form-control-feedback'
13434             };
13435
13436             inputblock = {
13437                 cls : 'has-feedback',
13438                 cn :  [
13439                     input,
13440                     feedback
13441                 ] 
13442             };  
13443         }
13444         
13445         
13446         if (this.before || this.after) {
13447             
13448             inputblock = {
13449                 cls : 'input-group',
13450                 cn :  [] 
13451             };
13452             if (this.before) {
13453                 inputblock.cn.push({
13454                     tag :'span',
13455                     cls : 'input-group-addon',
13456                     html : this.before
13457                 });
13458             }
13459             
13460             inputblock.cn.push(input);
13461             
13462             if(this.hasFeedback && !this.allowBlank){
13463                 inputblock.cls += ' has-feedback';
13464                 inputblock.cn.push(feedback);
13465             }
13466             
13467             if (this.after) {
13468                 inputblock.cn.push({
13469                     tag :'span',
13470                     cls : 'input-group-addon',
13471                     html : this.after
13472                 });
13473             }
13474             
13475         }
13476         
13477         if (align ==='left' && this.fieldLabel.length) {
13478             cfg.cn = [
13479                 {
13480                     tag: 'label',
13481                     'for' :  id,
13482                     cls : 'control-label',
13483                     html : this.fieldLabel
13484                 },
13485                 {
13486                     cls : "",
13487                     cn: [
13488                         inputblock
13489                     ]
13490                 }
13491
13492             ];
13493             
13494             if(this.labelWidth > 12){
13495                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13496             }
13497
13498             if(this.labelWidth < 13 && this.labelmd == 0){
13499                 this.labelmd = this.labelWidth;
13500             }
13501
13502             if(this.labellg > 0){
13503                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13504                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13505             }
13506
13507             if(this.labelmd > 0){
13508                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13509                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13510             }
13511
13512             if(this.labelsm > 0){
13513                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13514                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13515             }
13516
13517             if(this.labelxs > 0){
13518                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13519                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13520             }
13521             
13522         } else if ( this.fieldLabel.length) {
13523             cfg.cn = [
13524
13525                {
13526                    tag: 'label',
13527                    //cls : 'input-group-addon',
13528                    html : this.fieldLabel
13529
13530                },
13531
13532                inputblock
13533
13534            ];
13535
13536         } else {
13537
13538             cfg.cn = [
13539
13540                 inputblock
13541
13542             ];
13543                 
13544         }
13545         
13546         if (this.disabled) {
13547             input.disabled=true;
13548         }
13549         
13550         return cfg;
13551         
13552     },
13553     /**
13554      * return the real textarea element.
13555      */
13556     inputEl: function ()
13557     {
13558         return this.el.select('textarea.form-control',true).first();
13559     },
13560     
13561     /**
13562      * Clear any invalid styles/messages for this field
13563      */
13564     clearInvalid : function()
13565     {
13566         
13567         if(!this.el || this.preventMark){ // not rendered
13568             return;
13569         }
13570         
13571         var label = this.el.select('label', true).first();
13572         var icon = this.el.select('i.fa-star', true).first();
13573         
13574         if(label && icon){
13575             icon.remove();
13576         }
13577         this.el.removeClass( this.validClass);
13578         this.inputEl().removeClass('is-invalid');
13579          
13580         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13581             
13582             var feedback = this.el.select('.form-control-feedback', true).first();
13583             
13584             if(feedback){
13585                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13586             }
13587             
13588         }
13589         
13590         this.fireEvent('valid', this);
13591     },
13592     
13593      /**
13594      * Mark this field as valid
13595      */
13596     markValid : function()
13597     {
13598         if(!this.el  || this.preventMark){ // not rendered
13599             return;
13600         }
13601         
13602         this.el.removeClass([this.invalidClass, this.validClass]);
13603         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13604         
13605         var feedback = this.el.select('.form-control-feedback', true).first();
13606             
13607         if(feedback){
13608             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13609         }
13610
13611         if(this.disabled || this.allowBlank){
13612             return;
13613         }
13614         
13615         var label = this.el.select('label', true).first();
13616         var icon = this.el.select('i.fa-star', true).first();
13617         
13618         if(label && icon){
13619             icon.remove();
13620         }
13621         if (Roo.bootstrap.version == 3) {
13622             this.el.addClass(this.validClass);
13623         } else {
13624             this.inputEl().addClass('is-valid');
13625         }
13626         
13627         
13628         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13629             
13630             var feedback = this.el.select('.form-control-feedback', true).first();
13631             
13632             if(feedback){
13633                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13634                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13635             }
13636             
13637         }
13638         
13639         this.fireEvent('valid', this);
13640     },
13641     
13642      /**
13643      * Mark this field as invalid
13644      * @param {String} msg The validation message
13645      */
13646     markInvalid : function(msg)
13647     {
13648         if(!this.el  || this.preventMark){ // not rendered
13649             return;
13650         }
13651         
13652         this.el.removeClass([this.invalidClass, this.validClass]);
13653         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13654         
13655         var feedback = this.el.select('.form-control-feedback', true).first();
13656             
13657         if(feedback){
13658             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13659         }
13660
13661         if(this.disabled || this.allowBlank){
13662             return;
13663         }
13664         
13665         var label = this.el.select('label', true).first();
13666         var icon = this.el.select('i.fa-star', true).first();
13667         
13668         if(!this.getValue().length && label && !icon){
13669             this.el.createChild({
13670                 tag : 'i',
13671                 cls : 'text-danger fa fa-lg fa-star',
13672                 tooltip : 'This field is required',
13673                 style : 'margin-right:5px;'
13674             }, label, true);
13675         }
13676         
13677         if (Roo.bootstrap.version == 3) {
13678             this.el.addClass(this.invalidClass);
13679         } else {
13680             this.inputEl().addClass('is-invalid');
13681         }
13682         
13683         // fixme ... this may be depricated need to test..
13684         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13685             
13686             var feedback = this.el.select('.form-control-feedback', true).first();
13687             
13688             if(feedback){
13689                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13690                 
13691                 if(this.getValue().length || this.forceFeedback){
13692                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13693                 }
13694                 
13695             }
13696             
13697         }
13698         
13699         this.fireEvent('invalid', this, msg);
13700     }
13701 });
13702
13703  
13704 /*
13705  * - LGPL
13706  *
13707  * trigger field - base class for combo..
13708  * 
13709  */
13710  
13711 /**
13712  * @class Roo.bootstrap.form.TriggerField
13713  * @extends Roo.bootstrap.form.Input
13714  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13715  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13716  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13717  * for which you can provide a custom implementation.  For example:
13718  * <pre><code>
13719 var trigger = new Roo.bootstrap.form.TriggerField();
13720 trigger.onTriggerClick = myTriggerFn;
13721 trigger.applyTo('my-field');
13722 </code></pre>
13723  *
13724  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13725  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13726  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13727  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13728  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13729
13730  * @constructor
13731  * Create a new TriggerField.
13732  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13733  * to the base TextField)
13734  */
13735 Roo.bootstrap.form.TriggerField = function(config){
13736     this.mimicing = false;
13737     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13738 };
13739
13740 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13741     /**
13742      * @cfg {String} triggerClass A CSS class to apply to the trigger
13743      */
13744      /**
13745      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13746      */
13747     hideTrigger:false,
13748
13749     /**
13750      * @cfg {Boolean} removable (true|false) special filter default false
13751      */
13752     removable : false,
13753     
13754     /** @cfg {Boolean} grow @hide */
13755     /** @cfg {Number} growMin @hide */
13756     /** @cfg {Number} growMax @hide */
13757
13758     /**
13759      * @hide 
13760      * @method
13761      */
13762     autoSize: Roo.emptyFn,
13763     // private
13764     monitorTab : true,
13765     // private
13766     deferHeight : true,
13767
13768     
13769     actionMode : 'wrap',
13770     
13771     caret : false,
13772     
13773     
13774     getAutoCreate : function(){
13775        
13776         var align = this.labelAlign || this.parentLabelAlign();
13777         
13778         var id = Roo.id();
13779         
13780         var cfg = {
13781             cls: 'form-group' //input-group
13782         };
13783         
13784         
13785         var input =  {
13786             tag: 'input',
13787             id : id,
13788             type : this.inputType,
13789             cls : 'form-control',
13790             autocomplete: 'new-password',
13791             placeholder : this.placeholder || '' 
13792             
13793         };
13794         if (this.name) {
13795             input.name = this.name;
13796         }
13797         if (this.size) {
13798             input.cls += ' input-' + this.size;
13799         }
13800         
13801         if (this.disabled) {
13802             input.disabled=true;
13803         }
13804         
13805         var inputblock = input;
13806         
13807         if(this.hasFeedback && !this.allowBlank){
13808             
13809             var feedback = {
13810                 tag: 'span',
13811                 cls: 'glyphicon form-control-feedback'
13812             };
13813             
13814             if(this.removable && !this.editable  ){
13815                 inputblock = {
13816                     cls : 'has-feedback',
13817                     cn :  [
13818                         inputblock,
13819                         {
13820                             tag: 'button',
13821                             html : 'x',
13822                             cls : 'roo-combo-removable-btn close'
13823                         },
13824                         feedback
13825                     ] 
13826                 };
13827             } else {
13828                 inputblock = {
13829                     cls : 'has-feedback',
13830                     cn :  [
13831                         inputblock,
13832                         feedback
13833                     ] 
13834                 };
13835             }
13836
13837         } else {
13838             if(this.removable && !this.editable ){
13839                 inputblock = {
13840                     cls : 'roo-removable',
13841                     cn :  [
13842                         inputblock,
13843                         {
13844                             tag: 'button',
13845                             html : 'x',
13846                             cls : 'roo-combo-removable-btn close'
13847                         }
13848                     ] 
13849                 };
13850             }
13851         }
13852         
13853         if (this.before || this.after) {
13854             
13855             inputblock = {
13856                 cls : 'input-group',
13857                 cn :  [] 
13858             };
13859             if (this.before) {
13860                 inputblock.cn.push({
13861                     tag :'span',
13862                     cls : 'input-group-addon input-group-prepend input-group-text',
13863                     html : this.before
13864                 });
13865             }
13866             
13867             inputblock.cn.push(input);
13868             
13869             if(this.hasFeedback && !this.allowBlank){
13870                 inputblock.cls += ' has-feedback';
13871                 inputblock.cn.push(feedback);
13872             }
13873             
13874             if (this.after) {
13875                 inputblock.cn.push({
13876                     tag :'span',
13877                     cls : 'input-group-addon input-group-append input-group-text',
13878                     html : this.after
13879                 });
13880             }
13881             
13882         };
13883         
13884       
13885         
13886         var ibwrap = inputblock;
13887         
13888         if(this.multiple){
13889             ibwrap = {
13890                 tag: 'ul',
13891                 cls: 'roo-select2-choices',
13892                 cn:[
13893                     {
13894                         tag: 'li',
13895                         cls: 'roo-select2-search-field',
13896                         cn: [
13897
13898                             inputblock
13899                         ]
13900                     }
13901                 ]
13902             };
13903                 
13904         }
13905         
13906         var combobox = {
13907             cls: 'roo-select2-container input-group',
13908             cn: [
13909                  {
13910                     tag: 'input',
13911                     type : 'hidden',
13912                     cls: 'form-hidden-field'
13913                 },
13914                 ibwrap
13915             ]
13916         };
13917         
13918         if(!this.multiple && this.showToggleBtn){
13919             
13920             var caret = {
13921                         tag: 'span',
13922                         cls: 'caret'
13923              };
13924             if (this.caret != false) {
13925                 caret = {
13926                      tag: 'i',
13927                      cls: 'fa fa-' + this.caret
13928                 };
13929                 
13930             }
13931             
13932             combobox.cn.push({
13933                 tag :'span',
13934                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13935                 cn : [
13936                     Roo.bootstrap.version == 3 ? caret : '',
13937                     {
13938                         tag: 'span',
13939                         cls: 'combobox-clear',
13940                         cn  : [
13941                             {
13942                                 tag : 'i',
13943                                 cls: 'icon-remove'
13944                             }
13945                         ]
13946                     }
13947                 ]
13948
13949             })
13950         }
13951         
13952         if(this.multiple){
13953             combobox.cls += ' roo-select2-container-multi';
13954         }
13955          var indicator = {
13956             tag : 'i',
13957             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13958             tooltip : 'This field is required'
13959         };
13960         if (Roo.bootstrap.version == 4) {
13961             indicator = {
13962                 tag : 'i',
13963                 style : 'display:none'
13964             };
13965         }
13966         
13967         
13968         if (align ==='left' && this.fieldLabel.length) {
13969             
13970             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13971
13972             cfg.cn = [
13973                 indicator,
13974                 {
13975                     tag: 'label',
13976                     'for' :  id,
13977                     cls : 'control-label',
13978                     html : this.fieldLabel
13979
13980                 },
13981                 {
13982                     cls : "", 
13983                     cn: [
13984                         combobox
13985                     ]
13986                 }
13987
13988             ];
13989             
13990             var labelCfg = cfg.cn[1];
13991             var contentCfg = cfg.cn[2];
13992             
13993             if(this.indicatorpos == 'right'){
13994                 cfg.cn = [
13995                     {
13996                         tag: 'label',
13997                         'for' :  id,
13998                         cls : 'control-label',
13999                         cn : [
14000                             {
14001                                 tag : 'span',
14002                                 html : this.fieldLabel
14003                             },
14004                             indicator
14005                         ]
14006                     },
14007                     {
14008                         cls : "", 
14009                         cn: [
14010                             combobox
14011                         ]
14012                     }
14013
14014                 ];
14015                 
14016                 labelCfg = cfg.cn[0];
14017                 contentCfg = cfg.cn[1];
14018             }
14019             
14020             if(this.labelWidth > 12){
14021                 labelCfg.style = "width: " + this.labelWidth + 'px';
14022             }
14023             
14024             if(this.labelWidth < 13 && this.labelmd == 0){
14025                 this.labelmd = this.labelWidth;
14026             }
14027             
14028             if(this.labellg > 0){
14029                 labelCfg.cls += ' col-lg-' + this.labellg;
14030                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14031             }
14032             
14033             if(this.labelmd > 0){
14034                 labelCfg.cls += ' col-md-' + this.labelmd;
14035                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14036             }
14037             
14038             if(this.labelsm > 0){
14039                 labelCfg.cls += ' col-sm-' + this.labelsm;
14040                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14041             }
14042             
14043             if(this.labelxs > 0){
14044                 labelCfg.cls += ' col-xs-' + this.labelxs;
14045                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14046             }
14047             
14048         } else if ( this.fieldLabel.length) {
14049 //                Roo.log(" label");
14050             cfg.cn = [
14051                 indicator,
14052                {
14053                    tag: 'label',
14054                    //cls : 'input-group-addon',
14055                    html : this.fieldLabel
14056
14057                },
14058
14059                combobox
14060
14061             ];
14062             
14063             if(this.indicatorpos == 'right'){
14064                 
14065                 cfg.cn = [
14066                     {
14067                        tag: 'label',
14068                        cn : [
14069                            {
14070                                tag : 'span',
14071                                html : this.fieldLabel
14072                            },
14073                            indicator
14074                        ]
14075
14076                     },
14077                     combobox
14078
14079                 ];
14080
14081             }
14082
14083         } else {
14084             
14085 //                Roo.log(" no label && no align");
14086                 cfg = combobox
14087                      
14088                 
14089         }
14090         
14091         var settings=this;
14092         ['xs','sm','md','lg'].map(function(size){
14093             if (settings[size]) {
14094                 cfg.cls += ' col-' + size + '-' + settings[size];
14095             }
14096         });
14097         
14098         return cfg;
14099         
14100     },
14101     
14102     
14103     
14104     // private
14105     onResize : function(w, h){
14106 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14107 //        if(typeof w == 'number'){
14108 //            var x = w - this.trigger.getWidth();
14109 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14110 //            this.trigger.setStyle('left', x+'px');
14111 //        }
14112     },
14113
14114     // private
14115     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14116
14117     // private
14118     getResizeEl : function(){
14119         return this.inputEl();
14120     },
14121
14122     // private
14123     getPositionEl : function(){
14124         return this.inputEl();
14125     },
14126
14127     // private
14128     alignErrorIcon : function(){
14129         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14130     },
14131
14132     // private
14133     initEvents : function(){
14134         
14135         this.createList();
14136         
14137         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14138         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14139         if(!this.multiple && this.showToggleBtn){
14140             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14141             if(this.hideTrigger){
14142                 this.trigger.setDisplayed(false);
14143             }
14144             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14145         }
14146         
14147         if(this.multiple){
14148             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14149         }
14150         
14151         if(this.removable && !this.editable && !this.tickable){
14152             var close = this.closeTriggerEl();
14153             
14154             if(close){
14155                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14156                 close.on('click', this.removeBtnClick, this, close);
14157             }
14158         }
14159         
14160         //this.trigger.addClassOnOver('x-form-trigger-over');
14161         //this.trigger.addClassOnClick('x-form-trigger-click');
14162         
14163         //if(!this.width){
14164         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14165         //}
14166     },
14167     
14168     closeTriggerEl : function()
14169     {
14170         var close = this.el.select('.roo-combo-removable-btn', true).first();
14171         return close ? close : false;
14172     },
14173     
14174     removeBtnClick : function(e, h, el)
14175     {
14176         e.preventDefault();
14177         
14178         if(this.fireEvent("remove", this) !== false){
14179             this.reset();
14180             this.fireEvent("afterremove", this)
14181         }
14182     },
14183     
14184     createList : function()
14185     {
14186         this.list = Roo.get(document.body).createChild({
14187             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14188             cls: 'typeahead typeahead-long dropdown-menu shadow',
14189             style: 'display:none'
14190         });
14191         
14192         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14193         
14194     },
14195
14196     // private
14197     initTrigger : function(){
14198        
14199     },
14200
14201     // private
14202     onDestroy : function(){
14203         if(this.trigger){
14204             this.trigger.removeAllListeners();
14205           //  this.trigger.remove();
14206         }
14207         //if(this.wrap){
14208         //    this.wrap.remove();
14209         //}
14210         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14211     },
14212
14213     // private
14214     onFocus : function(){
14215         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14216         /*
14217         if(!this.mimicing){
14218             this.wrap.addClass('x-trigger-wrap-focus');
14219             this.mimicing = true;
14220             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14221             if(this.monitorTab){
14222                 this.el.on("keydown", this.checkTab, this);
14223             }
14224         }
14225         */
14226     },
14227
14228     // private
14229     checkTab : function(e){
14230         if(e.getKey() == e.TAB){
14231             this.triggerBlur();
14232         }
14233     },
14234
14235     // private
14236     onBlur : function(){
14237         // do nothing
14238     },
14239
14240     // private
14241     mimicBlur : function(e, t){
14242         /*
14243         if(!this.wrap.contains(t) && this.validateBlur()){
14244             this.triggerBlur();
14245         }
14246         */
14247     },
14248
14249     // private
14250     triggerBlur : function(){
14251         this.mimicing = false;
14252         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14253         if(this.monitorTab){
14254             this.el.un("keydown", this.checkTab, this);
14255         }
14256         //this.wrap.removeClass('x-trigger-wrap-focus');
14257         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14258     },
14259
14260     // private
14261     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14262     validateBlur : function(e, t){
14263         return true;
14264     },
14265
14266     // private
14267     onDisable : function(){
14268         this.inputEl().dom.disabled = true;
14269         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14270         //if(this.wrap){
14271         //    this.wrap.addClass('x-item-disabled');
14272         //}
14273     },
14274
14275     // private
14276     onEnable : function(){
14277         this.inputEl().dom.disabled = false;
14278         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14279         //if(this.wrap){
14280         //    this.el.removeClass('x-item-disabled');
14281         //}
14282     },
14283
14284     // private
14285     onShow : function(){
14286         var ae = this.getActionEl();
14287         
14288         if(ae){
14289             ae.dom.style.display = '';
14290             ae.dom.style.visibility = 'visible';
14291         }
14292     },
14293
14294     // private
14295     
14296     onHide : function(){
14297         var ae = this.getActionEl();
14298         ae.dom.style.display = 'none';
14299     },
14300
14301     /**
14302      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14303      * by an implementing function.
14304      * @method
14305      * @param {EventObject} e
14306      */
14307     onTriggerClick : Roo.emptyFn
14308 });
14309  
14310 /*
14311 * Licence: LGPL
14312 */
14313
14314 /**
14315  * @class Roo.bootstrap.form.CardUploader
14316  * @extends Roo.bootstrap.Button
14317  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14318  * @cfg {Number} errorTimeout default 3000
14319  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14320  * @cfg {Array}  html The button text.
14321
14322  *
14323  * @constructor
14324  * Create a new CardUploader
14325  * @param {Object} config The config object
14326  */
14327
14328 Roo.bootstrap.form.CardUploader = function(config){
14329     
14330  
14331     
14332     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14333     
14334     
14335     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14336         return r.data.id
14337      });
14338     
14339      this.addEvents({
14340          // raw events
14341         /**
14342          * @event preview
14343          * When a image is clicked on - and needs to display a slideshow or similar..
14344          * @param {Roo.bootstrap.Card} this
14345          * @param {Object} The image information data 
14346          *
14347          */
14348         'preview' : true,
14349          /**
14350          * @event download
14351          * When a the download link is clicked
14352          * @param {Roo.bootstrap.Card} this
14353          * @param {Object} The image information data  contains 
14354          */
14355         'download' : true
14356         
14357     });
14358 };
14359  
14360 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14361     
14362      
14363     errorTimeout : 3000,
14364      
14365     images : false,
14366    
14367     fileCollection : false,
14368     allowBlank : true,
14369     
14370     getAutoCreate : function()
14371     {
14372         
14373         var cfg =  {
14374             cls :'form-group' ,
14375             cn : [
14376                
14377                 {
14378                     tag: 'label',
14379                    //cls : 'input-group-addon',
14380                     html : this.fieldLabel
14381
14382                 },
14383
14384                 {
14385                     tag: 'input',
14386                     type : 'hidden',
14387                     name : this.name,
14388                     value : this.value,
14389                     cls : 'd-none  form-control'
14390                 },
14391                 
14392                 {
14393                     tag: 'input',
14394                     multiple : 'multiple',
14395                     type : 'file',
14396                     cls : 'd-none  roo-card-upload-selector'
14397                 },
14398                 
14399                 {
14400                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14401                 },
14402                 {
14403                     cls : 'card-columns roo-card-uploader-container'
14404                 }
14405
14406             ]
14407         };
14408            
14409          
14410         return cfg;
14411     },
14412     
14413     getChildContainer : function() /// what children are added to.
14414     {
14415         return this.containerEl;
14416     },
14417    
14418     getButtonContainer : function() /// what children are added to.
14419     {
14420         return this.el.select(".roo-card-uploader-button-container").first();
14421     },
14422    
14423     initEvents : function()
14424     {
14425         
14426         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14427         
14428         var t = this;
14429         this.addxtype({
14430             xns: Roo.bootstrap,
14431
14432             xtype : 'Button',
14433             container_method : 'getButtonContainer' ,            
14434             html :  this.html, // fix changable?
14435             cls : 'w-100 ',
14436             listeners : {
14437                 'click' : function(btn, e) {
14438                     t.onClick(e);
14439                 }
14440             }
14441         });
14442         
14443         
14444         
14445         
14446         this.urlAPI = (window.createObjectURL && window) || 
14447                                 (window.URL && URL.revokeObjectURL && URL) || 
14448                                 (window.webkitURL && webkitURL);
14449                         
14450          
14451          
14452          
14453         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14454         
14455         this.selectorEl.on('change', this.onFileSelected, this);
14456         if (this.images) {
14457             var t = this;
14458             this.images.forEach(function(img) {
14459                 t.addCard(img)
14460             });
14461             this.images = false;
14462         }
14463         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14464          
14465        
14466     },
14467     
14468    
14469     onClick : function(e)
14470     {
14471         e.preventDefault();
14472          
14473         this.selectorEl.dom.click();
14474          
14475     },
14476     
14477     onFileSelected : function(e)
14478     {
14479         e.preventDefault();
14480         
14481         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14482             return;
14483         }
14484         
14485         Roo.each(this.selectorEl.dom.files, function(file){    
14486             this.addFile(file);
14487         }, this);
14488          
14489     },
14490     
14491       
14492     
14493       
14494     
14495     addFile : function(file)
14496     {
14497            
14498         if(typeof(file) === 'string'){
14499             throw "Add file by name?"; // should not happen
14500             return;
14501         }
14502         
14503         if(!file || !this.urlAPI){
14504             return;
14505         }
14506         
14507         // file;
14508         // file.type;
14509         
14510         var _this = this;
14511         
14512         
14513         var url = _this.urlAPI.createObjectURL( file);
14514            
14515         this.addCard({
14516             id : Roo.bootstrap.form.CardUploader.ID--,
14517             is_uploaded : false,
14518             src : url,
14519             srcfile : file,
14520             title : file.name,
14521             mimetype : file.type,
14522             preview : false,
14523             is_deleted : 0
14524         });
14525         
14526     },
14527     
14528     /**
14529      * addCard - add an Attachment to the uploader
14530      * @param data - the data about the image to upload
14531      *
14532      * {
14533           id : 123
14534           title : "Title of file",
14535           is_uploaded : false,
14536           src : "http://.....",
14537           srcfile : { the File upload object },
14538           mimetype : file.type,
14539           preview : false,
14540           is_deleted : 0
14541           .. any other data...
14542         }
14543      *
14544      * 
14545     */
14546     
14547     addCard : function (data)
14548     {
14549         // hidden input element?
14550         // if the file is not an image...
14551         //then we need to use something other that and header_image
14552         var t = this;
14553         //   remove.....
14554         var footer = [
14555             {
14556                 xns : Roo.bootstrap,
14557                 xtype : 'CardFooter',
14558                  items: [
14559                     {
14560                         xns : Roo.bootstrap,
14561                         xtype : 'Element',
14562                         cls : 'd-flex',
14563                         items : [
14564                             
14565                             {
14566                                 xns : Roo.bootstrap,
14567                                 xtype : 'Button',
14568                                 html : String.format("<small>{0}</small>", data.title),
14569                                 cls : 'col-10 text-left',
14570                                 size: 'sm',
14571                                 weight: 'link',
14572                                 fa : 'download',
14573                                 listeners : {
14574                                     click : function() {
14575                                      
14576                                         t.fireEvent( "download", t, data );
14577                                     }
14578                                 }
14579                             },
14580                           
14581                             {
14582                                 xns : Roo.bootstrap,
14583                                 xtype : 'Button',
14584                                 style: 'max-height: 28px; ',
14585                                 size : 'sm',
14586                                 weight: 'danger',
14587                                 cls : 'col-2',
14588                                 fa : 'times',
14589                                 listeners : {
14590                                     click : function() {
14591                                         t.removeCard(data.id)
14592                                     }
14593                                 }
14594                             }
14595                         ]
14596                     }
14597                     
14598                 ] 
14599             }
14600             
14601         ];
14602         
14603         var cn = this.addxtype(
14604             {
14605                  
14606                 xns : Roo.bootstrap,
14607                 xtype : 'Card',
14608                 closeable : true,
14609                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14610                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14611                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14612                 data : data,
14613                 html : false,
14614                  
14615                 items : footer,
14616                 initEvents : function() {
14617                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14618                     var card = this;
14619                     this.imgEl = this.el.select('.card-img-top').first();
14620                     if (this.imgEl) {
14621                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14622                         this.imgEl.set({ 'pointer' : 'cursor' });
14623                                   
14624                     }
14625                     this.getCardFooter().addClass('p-1');
14626                     
14627                   
14628                 }
14629                 
14630             }
14631         );
14632         // dont' really need ot update items.
14633         // this.items.push(cn);
14634         this.fileCollection.add(cn);
14635         
14636         if (!data.srcfile) {
14637             this.updateInput();
14638             return;
14639         }
14640             
14641         var _t = this;
14642         var reader = new FileReader();
14643         reader.addEventListener("load", function() {  
14644             data.srcdata =  reader.result;
14645             _t.updateInput();
14646         });
14647         reader.readAsDataURL(data.srcfile);
14648         
14649         
14650         
14651     },
14652     removeCard : function(id)
14653     {
14654         
14655         var card  = this.fileCollection.get(id);
14656         card.data.is_deleted = 1;
14657         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14658         //this.fileCollection.remove(card);
14659         //this.items = this.items.filter(function(e) { return e != card });
14660         // dont' really need ot update items.
14661         card.el.dom.parentNode.removeChild(card.el.dom);
14662         this.updateInput();
14663
14664         
14665     },
14666     reset: function()
14667     {
14668         this.fileCollection.each(function(card) {
14669             if (card.el.dom && card.el.dom.parentNode) {
14670                 card.el.dom.parentNode.removeChild(card.el.dom);
14671             }
14672         });
14673         this.fileCollection.clear();
14674         this.updateInput();
14675     },
14676     
14677     updateInput : function()
14678     {
14679          var data = [];
14680         this.fileCollection.each(function(e) {
14681             data.push(e.data);
14682             
14683         });
14684         this.inputEl().dom.value = JSON.stringify(data);
14685         
14686         
14687         
14688     }
14689     
14690     
14691 });
14692
14693
14694 Roo.bootstrap.form.CardUploader.ID = -1;/*
14695  * Based on:
14696  * Ext JS Library 1.1.1
14697  * Copyright(c) 2006-2007, Ext JS, LLC.
14698  *
14699  * Originally Released Under LGPL - original licence link has changed is not relivant.
14700  *
14701  * Fork - LGPL
14702  * <script type="text/javascript">
14703  */
14704
14705
14706 /**
14707  * @class Roo.data.SortTypes
14708  * @static
14709  * Defines the default sorting (casting?) comparison functions used when sorting data.
14710  */
14711 Roo.data.SortTypes = {
14712     /**
14713      * Default sort that does nothing
14714      * @param {Mixed} s The value being converted
14715      * @return {Mixed} The comparison value
14716      */
14717     none : function(s){
14718         return s;
14719     },
14720     
14721     /**
14722      * The regular expression used to strip tags
14723      * @type {RegExp}
14724      * @property
14725      */
14726     stripTagsRE : /<\/?[^>]+>/gi,
14727     
14728     /**
14729      * Strips all HTML tags to sort on text only
14730      * @param {Mixed} s The value being converted
14731      * @return {String} The comparison value
14732      */
14733     asText : function(s){
14734         return String(s).replace(this.stripTagsRE, "");
14735     },
14736     
14737     /**
14738      * Strips all HTML tags to sort on text only - Case insensitive
14739      * @param {Mixed} s The value being converted
14740      * @return {String} The comparison value
14741      */
14742     asUCText : function(s){
14743         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14744     },
14745     
14746     /**
14747      * Case insensitive string
14748      * @param {Mixed} s The value being converted
14749      * @return {String} The comparison value
14750      */
14751     asUCString : function(s) {
14752         return String(s).toUpperCase();
14753     },
14754     
14755     /**
14756      * Date sorting
14757      * @param {Mixed} s The value being converted
14758      * @return {Number} The comparison value
14759      */
14760     asDate : function(s) {
14761         if(!s){
14762             return 0;
14763         }
14764         if(s instanceof Date){
14765             return s.getTime();
14766         }
14767         return Date.parse(String(s));
14768     },
14769     
14770     /**
14771      * Float sorting
14772      * @param {Mixed} s The value being converted
14773      * @return {Float} The comparison value
14774      */
14775     asFloat : function(s) {
14776         var val = parseFloat(String(s).replace(/,/g, ""));
14777         if(isNaN(val)) {
14778             val = 0;
14779         }
14780         return val;
14781     },
14782     
14783     /**
14784      * Integer sorting
14785      * @param {Mixed} s The value being converted
14786      * @return {Number} The comparison value
14787      */
14788     asInt : function(s) {
14789         var val = parseInt(String(s).replace(/,/g, ""));
14790         if(isNaN(val)) {
14791             val = 0;
14792         }
14793         return val;
14794     }
14795 };/*
14796  * Based on:
14797  * Ext JS Library 1.1.1
14798  * Copyright(c) 2006-2007, Ext JS, LLC.
14799  *
14800  * Originally Released Under LGPL - original licence link has changed is not relivant.
14801  *
14802  * Fork - LGPL
14803  * <script type="text/javascript">
14804  */
14805
14806 /**
14807 * @class Roo.data.Record
14808  * Instances of this class encapsulate both record <em>definition</em> information, and record
14809  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14810  * to access Records cached in an {@link Roo.data.Store} object.<br>
14811  * <p>
14812  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14813  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14814  * objects.<br>
14815  * <p>
14816  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14817  * @constructor
14818  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14819  * {@link #create}. The parameters are the same.
14820  * @param {Array} data An associative Array of data values keyed by the field name.
14821  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14822  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14823  * not specified an integer id is generated.
14824  */
14825 Roo.data.Record = function(data, id){
14826     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14827     this.data = data;
14828 };
14829
14830 /**
14831  * Generate a constructor for a specific record layout.
14832  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14833  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14834  * Each field definition object may contain the following properties: <ul>
14835  * <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,
14836  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14837  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14838  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14839  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14840  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14841  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14842  * this may be omitted.</p></li>
14843  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14844  * <ul><li>auto (Default, implies no conversion)</li>
14845  * <li>string</li>
14846  * <li>int</li>
14847  * <li>float</li>
14848  * <li>boolean</li>
14849  * <li>date</li></ul></p></li>
14850  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14851  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14852  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14853  * by the Reader into an object that will be stored in the Record. It is passed the
14854  * following parameters:<ul>
14855  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14856  * </ul></p></li>
14857  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14858  * </ul>
14859  * <br>usage:<br><pre><code>
14860 var TopicRecord = Roo.data.Record.create(
14861     {name: 'title', mapping: 'topic_title'},
14862     {name: 'author', mapping: 'username'},
14863     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14864     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14865     {name: 'lastPoster', mapping: 'user2'},
14866     {name: 'excerpt', mapping: 'post_text'}
14867 );
14868
14869 var myNewRecord = new TopicRecord({
14870     title: 'Do my job please',
14871     author: 'noobie',
14872     totalPosts: 1,
14873     lastPost: new Date(),
14874     lastPoster: 'Animal',
14875     excerpt: 'No way dude!'
14876 });
14877 myStore.add(myNewRecord);
14878 </code></pre>
14879  * @method create
14880  * @static
14881  */
14882 Roo.data.Record.create = function(o){
14883     var f = function(){
14884         f.superclass.constructor.apply(this, arguments);
14885     };
14886     Roo.extend(f, Roo.data.Record);
14887     var p = f.prototype;
14888     p.fields = new Roo.util.MixedCollection(false, function(field){
14889         return field.name;
14890     });
14891     for(var i = 0, len = o.length; i < len; i++){
14892         p.fields.add(new Roo.data.Field(o[i]));
14893     }
14894     f.getField = function(name){
14895         return p.fields.get(name);  
14896     };
14897     return f;
14898 };
14899
14900 Roo.data.Record.AUTO_ID = 1000;
14901 Roo.data.Record.EDIT = 'edit';
14902 Roo.data.Record.REJECT = 'reject';
14903 Roo.data.Record.COMMIT = 'commit';
14904
14905 Roo.data.Record.prototype = {
14906     /**
14907      * Readonly flag - true if this record has been modified.
14908      * @type Boolean
14909      */
14910     dirty : false,
14911     editing : false,
14912     error: null,
14913     modified: null,
14914
14915     // private
14916     join : function(store){
14917         this.store = store;
14918     },
14919
14920     /**
14921      * Set the named field to the specified value.
14922      * @param {String} name The name of the field to set.
14923      * @param {Object} value The value to set the field to.
14924      */
14925     set : function(name, value){
14926         if(this.data[name] == value){
14927             return;
14928         }
14929         this.dirty = true;
14930         if(!this.modified){
14931             this.modified = {};
14932         }
14933         if(typeof this.modified[name] == 'undefined'){
14934             this.modified[name] = this.data[name];
14935         }
14936         this.data[name] = value;
14937         if(!this.editing && this.store){
14938             this.store.afterEdit(this);
14939         }       
14940     },
14941
14942     /**
14943      * Get the value of the named field.
14944      * @param {String} name The name of the field to get the value of.
14945      * @return {Object} The value of the field.
14946      */
14947     get : function(name){
14948         return this.data[name]; 
14949     },
14950
14951     // private
14952     beginEdit : function(){
14953         this.editing = true;
14954         this.modified = {}; 
14955     },
14956
14957     // private
14958     cancelEdit : function(){
14959         this.editing = false;
14960         delete this.modified;
14961     },
14962
14963     // private
14964     endEdit : function(){
14965         this.editing = false;
14966         if(this.dirty && this.store){
14967             this.store.afterEdit(this);
14968         }
14969     },
14970
14971     /**
14972      * Usually called by the {@link Roo.data.Store} which owns the Record.
14973      * Rejects all changes made to the Record since either creation, or the last commit operation.
14974      * Modified fields are reverted to their original values.
14975      * <p>
14976      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14977      * of reject operations.
14978      */
14979     reject : function(){
14980         var m = this.modified;
14981         for(var n in m){
14982             if(typeof m[n] != "function"){
14983                 this.data[n] = m[n];
14984             }
14985         }
14986         this.dirty = false;
14987         delete this.modified;
14988         this.editing = false;
14989         if(this.store){
14990             this.store.afterReject(this);
14991         }
14992     },
14993
14994     /**
14995      * Usually called by the {@link Roo.data.Store} which owns the Record.
14996      * Commits all changes made to the Record since either creation, or the last commit operation.
14997      * <p>
14998      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14999      * of commit operations.
15000      */
15001     commit : function(){
15002         this.dirty = false;
15003         delete this.modified;
15004         this.editing = false;
15005         if(this.store){
15006             this.store.afterCommit(this);
15007         }
15008     },
15009
15010     // private
15011     hasError : function(){
15012         return this.error != null;
15013     },
15014
15015     // private
15016     clearError : function(){
15017         this.error = null;
15018     },
15019
15020     /**
15021      * Creates a copy of this record.
15022      * @param {String} id (optional) A new record id if you don't want to use this record's id
15023      * @return {Record}
15024      */
15025     copy : function(newId) {
15026         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15027     }
15028 };/*
15029  * Based on:
15030  * Ext JS Library 1.1.1
15031  * Copyright(c) 2006-2007, Ext JS, LLC.
15032  *
15033  * Originally Released Under LGPL - original licence link has changed is not relivant.
15034  *
15035  * Fork - LGPL
15036  * <script type="text/javascript">
15037  */
15038
15039
15040
15041 /**
15042  * @class Roo.data.Store
15043  * @extends Roo.util.Observable
15044  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15045  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15046  * <p>
15047  * 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
15048  * has no knowledge of the format of the data returned by the Proxy.<br>
15049  * <p>
15050  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15051  * instances from the data object. These records are cached and made available through accessor functions.
15052  * @constructor
15053  * Creates a new Store.
15054  * @param {Object} config A config object containing the objects needed for the Store to access data,
15055  * and read the data into Records.
15056  */
15057 Roo.data.Store = function(config){
15058     this.data = new Roo.util.MixedCollection(false);
15059     this.data.getKey = function(o){
15060         return o.id;
15061     };
15062     this.baseParams = {};
15063     // private
15064     this.paramNames = {
15065         "start" : "start",
15066         "limit" : "limit",
15067         "sort" : "sort",
15068         "dir" : "dir",
15069         "multisort" : "_multisort"
15070     };
15071
15072     if(config && config.data){
15073         this.inlineData = config.data;
15074         delete config.data;
15075     }
15076
15077     Roo.apply(this, config);
15078     
15079     if(this.reader){ // reader passed
15080         this.reader = Roo.factory(this.reader, Roo.data);
15081         this.reader.xmodule = this.xmodule || false;
15082         if(!this.recordType){
15083             this.recordType = this.reader.recordType;
15084         }
15085         if(this.reader.onMetaChange){
15086             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15087         }
15088     }
15089
15090     if(this.recordType){
15091         this.fields = this.recordType.prototype.fields;
15092     }
15093     this.modified = [];
15094
15095     this.addEvents({
15096         /**
15097          * @event datachanged
15098          * Fires when the data cache has changed, and a widget which is using this Store
15099          * as a Record cache should refresh its view.
15100          * @param {Store} this
15101          */
15102         datachanged : true,
15103         /**
15104          * @event metachange
15105          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15106          * @param {Store} this
15107          * @param {Object} meta The JSON metadata
15108          */
15109         metachange : true,
15110         /**
15111          * @event add
15112          * Fires when Records have been added to the Store
15113          * @param {Store} this
15114          * @param {Roo.data.Record[]} records The array of Records added
15115          * @param {Number} index The index at which the record(s) were added
15116          */
15117         add : true,
15118         /**
15119          * @event remove
15120          * Fires when a Record has been removed from the Store
15121          * @param {Store} this
15122          * @param {Roo.data.Record} record The Record that was removed
15123          * @param {Number} index The index at which the record was removed
15124          */
15125         remove : true,
15126         /**
15127          * @event update
15128          * Fires when a Record has been updated
15129          * @param {Store} this
15130          * @param {Roo.data.Record} record The Record that was updated
15131          * @param {String} operation The update operation being performed.  Value may be one of:
15132          * <pre><code>
15133  Roo.data.Record.EDIT
15134  Roo.data.Record.REJECT
15135  Roo.data.Record.COMMIT
15136          * </code></pre>
15137          */
15138         update : true,
15139         /**
15140          * @event clear
15141          * Fires when the data cache has been cleared.
15142          * @param {Store} this
15143          */
15144         clear : true,
15145         /**
15146          * @event beforeload
15147          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15148          * the load action will be canceled.
15149          * @param {Store} this
15150          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15151          */
15152         beforeload : true,
15153         /**
15154          * @event beforeloadadd
15155          * Fires after a new set of Records has been loaded.
15156          * @param {Store} this
15157          * @param {Roo.data.Record[]} records The Records that were loaded
15158          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15159          */
15160         beforeloadadd : true,
15161         /**
15162          * @event load
15163          * Fires after a new set of Records has been loaded, before they are added to the store.
15164          * @param {Store} this
15165          * @param {Roo.data.Record[]} records The Records that were loaded
15166          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15167          * @params {Object} return from reader
15168          */
15169         load : true,
15170         /**
15171          * @event loadexception
15172          * Fires if an exception occurs in the Proxy during loading.
15173          * Called with the signature of the Proxy's "loadexception" event.
15174          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15175          * 
15176          * @param {Proxy} 
15177          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15178          * @param {Object} load options 
15179          * @param {Object} jsonData from your request (normally this contains the Exception)
15180          */
15181         loadexception : true
15182     });
15183     
15184     if(this.proxy){
15185         this.proxy = Roo.factory(this.proxy, Roo.data);
15186         this.proxy.xmodule = this.xmodule || false;
15187         this.relayEvents(this.proxy,  ["loadexception"]);
15188     }
15189     this.sortToggle = {};
15190     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15191
15192     Roo.data.Store.superclass.constructor.call(this);
15193
15194     if(this.inlineData){
15195         this.loadData(this.inlineData);
15196         delete this.inlineData;
15197     }
15198 };
15199
15200 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15201      /**
15202     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15203     * without a remote query - used by combo/forms at present.
15204     */
15205     
15206     /**
15207     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15208     */
15209     /**
15210     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15211     */
15212     /**
15213     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15214     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15215     */
15216     /**
15217     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15218     * on any HTTP request
15219     */
15220     /**
15221     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15222     */
15223     /**
15224     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15225     */
15226     multiSort: false,
15227     /**
15228     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15229     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15230     */
15231     remoteSort : false,
15232
15233     /**
15234     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15235      * loaded or when a record is removed. (defaults to false).
15236     */
15237     pruneModifiedRecords : false,
15238
15239     // private
15240     lastOptions : null,
15241
15242     /**
15243      * Add Records to the Store and fires the add event.
15244      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15245      */
15246     add : function(records){
15247         records = [].concat(records);
15248         for(var i = 0, len = records.length; i < len; i++){
15249             records[i].join(this);
15250         }
15251         var index = this.data.length;
15252         this.data.addAll(records);
15253         this.fireEvent("add", this, records, index);
15254     },
15255
15256     /**
15257      * Remove a Record from the Store and fires the remove event.
15258      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15259      */
15260     remove : function(record){
15261         var index = this.data.indexOf(record);
15262         this.data.removeAt(index);
15263  
15264         if(this.pruneModifiedRecords){
15265             this.modified.remove(record);
15266         }
15267         this.fireEvent("remove", this, record, index);
15268     },
15269
15270     /**
15271      * Remove all Records from the Store and fires the clear event.
15272      */
15273     removeAll : function(){
15274         this.data.clear();
15275         if(this.pruneModifiedRecords){
15276             this.modified = [];
15277         }
15278         this.fireEvent("clear", this);
15279     },
15280
15281     /**
15282      * Inserts Records to the Store at the given index and fires the add event.
15283      * @param {Number} index The start index at which to insert the passed Records.
15284      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15285      */
15286     insert : function(index, records){
15287         records = [].concat(records);
15288         for(var i = 0, len = records.length; i < len; i++){
15289             this.data.insert(index, records[i]);
15290             records[i].join(this);
15291         }
15292         this.fireEvent("add", this, records, index);
15293     },
15294
15295     /**
15296      * Get the index within the cache of the passed Record.
15297      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15298      * @return {Number} The index of the passed Record. Returns -1 if not found.
15299      */
15300     indexOf : function(record){
15301         return this.data.indexOf(record);
15302     },
15303
15304     /**
15305      * Get the index within the cache of the Record with the passed id.
15306      * @param {String} id The id of the Record to find.
15307      * @return {Number} The index of the Record. Returns -1 if not found.
15308      */
15309     indexOfId : function(id){
15310         return this.data.indexOfKey(id);
15311     },
15312
15313     /**
15314      * Get the Record with the specified id.
15315      * @param {String} id The id of the Record to find.
15316      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15317      */
15318     getById : function(id){
15319         return this.data.key(id);
15320     },
15321
15322     /**
15323      * Get the Record at the specified index.
15324      * @param {Number} index The index of the Record to find.
15325      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15326      */
15327     getAt : function(index){
15328         return this.data.itemAt(index);
15329     },
15330
15331     /**
15332      * Returns a range of Records between specified indices.
15333      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15334      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15335      * @return {Roo.data.Record[]} An array of Records
15336      */
15337     getRange : function(start, end){
15338         return this.data.getRange(start, end);
15339     },
15340
15341     // private
15342     storeOptions : function(o){
15343         o = Roo.apply({}, o);
15344         delete o.callback;
15345         delete o.scope;
15346         this.lastOptions = o;
15347     },
15348
15349     /**
15350      * Loads the Record cache from the configured Proxy using the configured Reader.
15351      * <p>
15352      * If using remote paging, then the first load call must specify the <em>start</em>
15353      * and <em>limit</em> properties in the options.params property to establish the initial
15354      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15355      * <p>
15356      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15357      * and this call will return before the new data has been loaded. Perform any post-processing
15358      * in a callback function, or in a "load" event handler.</strong>
15359      * <p>
15360      * @param {Object} options An object containing properties which control loading options:<ul>
15361      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15362      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15363      * <pre>
15364                 {
15365                     data : data,  // array of key=>value data like JsonReader
15366                     total : data.length,
15367                     success : true
15368                     
15369                 }
15370         </pre>
15371             }.</li>
15372      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15373      * passed the following arguments:<ul>
15374      * <li>r : Roo.data.Record[]</li>
15375      * <li>options: Options object from the load call</li>
15376      * <li>success: Boolean success indicator</li></ul></li>
15377      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15378      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15379      * </ul>
15380      */
15381     load : function(options){
15382         options = options || {};
15383         if(this.fireEvent("beforeload", this, options) !== false){
15384             this.storeOptions(options);
15385             var p = Roo.apply(options.params || {}, this.baseParams);
15386             // if meta was not loaded from remote source.. try requesting it.
15387             if (!this.reader.metaFromRemote) {
15388                 p._requestMeta = 1;
15389             }
15390             if(this.sortInfo && this.remoteSort){
15391                 var pn = this.paramNames;
15392                 p[pn["sort"]] = this.sortInfo.field;
15393                 p[pn["dir"]] = this.sortInfo.direction;
15394             }
15395             if (this.multiSort) {
15396                 var pn = this.paramNames;
15397                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15398             }
15399             
15400             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15401         }
15402     },
15403
15404     /**
15405      * Reloads the Record cache from the configured Proxy using the configured Reader and
15406      * the options from the last load operation performed.
15407      * @param {Object} options (optional) An object containing properties which may override the options
15408      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15409      * the most recently used options are reused).
15410      */
15411     reload : function(options){
15412         this.load(Roo.applyIf(options||{}, this.lastOptions));
15413     },
15414
15415     // private
15416     // Called as a callback by the Reader during a load operation.
15417     loadRecords : function(o, options, success){
15418          
15419         if(!o){
15420             if(success !== false){
15421                 this.fireEvent("load", this, [], options, o);
15422             }
15423             if(options.callback){
15424                 options.callback.call(options.scope || this, [], options, false);
15425             }
15426             return;
15427         }
15428         // if data returned failure - throw an exception.
15429         if (o.success === false) {
15430             // show a message if no listener is registered.
15431             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15432                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15433             }
15434             // loadmask wil be hooked into this..
15435             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15436             return;
15437         }
15438         var r = o.records, t = o.totalRecords || r.length;
15439         
15440         this.fireEvent("beforeloadadd", this, r, options, o);
15441         
15442         if(!options || options.add !== true){
15443             if(this.pruneModifiedRecords){
15444                 this.modified = [];
15445             }
15446             for(var i = 0, len = r.length; i < len; i++){
15447                 r[i].join(this);
15448             }
15449             if(this.snapshot){
15450                 this.data = this.snapshot;
15451                 delete this.snapshot;
15452             }
15453             this.data.clear();
15454             this.data.addAll(r);
15455             this.totalLength = t;
15456             this.applySort();
15457             this.fireEvent("datachanged", this);
15458         }else{
15459             this.totalLength = Math.max(t, this.data.length+r.length);
15460             this.add(r);
15461         }
15462         
15463         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15464                 
15465             var e = new Roo.data.Record({});
15466
15467             e.set(this.parent.displayField, this.parent.emptyTitle);
15468             e.set(this.parent.valueField, '');
15469
15470             this.insert(0, e);
15471         }
15472             
15473         this.fireEvent("load", this, r, options, o);
15474         if(options.callback){
15475             options.callback.call(options.scope || this, r, options, true);
15476         }
15477     },
15478
15479
15480     /**
15481      * Loads data from a passed data block. A Reader which understands the format of the data
15482      * must have been configured in the constructor.
15483      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15484      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15485      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15486      */
15487     loadData : function(o, append){
15488         var r = this.reader.readRecords(o);
15489         this.loadRecords(r, {add: append}, true);
15490     },
15491     
15492      /**
15493      * using 'cn' the nested child reader read the child array into it's child stores.
15494      * @param {Object} rec The record with a 'children array
15495      */
15496     loadDataFromChildren : function(rec)
15497     {
15498         this.loadData(this.reader.toLoadData(rec));
15499     },
15500     
15501
15502     /**
15503      * Gets the number of cached records.
15504      * <p>
15505      * <em>If using paging, this may not be the total size of the dataset. If the data object
15506      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15507      * the data set size</em>
15508      */
15509     getCount : function(){
15510         return this.data.length || 0;
15511     },
15512
15513     /**
15514      * Gets the total number of records in the dataset as returned by the server.
15515      * <p>
15516      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15517      * the dataset size</em>
15518      */
15519     getTotalCount : function(){
15520         return this.totalLength || 0;
15521     },
15522
15523     /**
15524      * Returns the sort state of the Store as an object with two properties:
15525      * <pre><code>
15526  field {String} The name of the field by which the Records are sorted
15527  direction {String} The sort order, "ASC" or "DESC"
15528      * </code></pre>
15529      */
15530     getSortState : function(){
15531         return this.sortInfo;
15532     },
15533
15534     // private
15535     applySort : function(){
15536         if(this.sortInfo && !this.remoteSort){
15537             var s = this.sortInfo, f = s.field;
15538             var st = this.fields.get(f).sortType;
15539             var fn = function(r1, r2){
15540                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15541                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15542             };
15543             this.data.sort(s.direction, fn);
15544             if(this.snapshot && this.snapshot != this.data){
15545                 this.snapshot.sort(s.direction, fn);
15546             }
15547         }
15548     },
15549
15550     /**
15551      * Sets the default sort column and order to be used by the next load operation.
15552      * @param {String} fieldName The name of the field to sort by.
15553      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15554      */
15555     setDefaultSort : function(field, dir){
15556         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15557     },
15558
15559     /**
15560      * Sort the Records.
15561      * If remote sorting is used, the sort is performed on the server, and the cache is
15562      * reloaded. If local sorting is used, the cache is sorted internally.
15563      * @param {String} fieldName The name of the field to sort by.
15564      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15565      */
15566     sort : function(fieldName, dir){
15567         var f = this.fields.get(fieldName);
15568         if(!dir){
15569             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15570             
15571             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15572                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15573             }else{
15574                 dir = f.sortDir;
15575             }
15576         }
15577         this.sortToggle[f.name] = dir;
15578         this.sortInfo = {field: f.name, direction: dir};
15579         if(!this.remoteSort){
15580             this.applySort();
15581             this.fireEvent("datachanged", this);
15582         }else{
15583             this.load(this.lastOptions);
15584         }
15585     },
15586
15587     /**
15588      * Calls the specified function for each of the Records in the cache.
15589      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15590      * Returning <em>false</em> aborts and exits the iteration.
15591      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15592      */
15593     each : function(fn, scope){
15594         this.data.each(fn, scope);
15595     },
15596
15597     /**
15598      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15599      * (e.g., during paging).
15600      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15601      */
15602     getModifiedRecords : function(){
15603         return this.modified;
15604     },
15605
15606     // private
15607     createFilterFn : function(property, value, anyMatch){
15608         if(!value.exec){ // not a regex
15609             value = String(value);
15610             if(value.length == 0){
15611                 return false;
15612             }
15613             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15614         }
15615         return function(r){
15616             return value.test(r.data[property]);
15617         };
15618     },
15619
15620     /**
15621      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15622      * @param {String} property A field on your records
15623      * @param {Number} start The record index to start at (defaults to 0)
15624      * @param {Number} end The last record index to include (defaults to length - 1)
15625      * @return {Number} The sum
15626      */
15627     sum : function(property, start, end){
15628         var rs = this.data.items, v = 0;
15629         start = start || 0;
15630         end = (end || end === 0) ? end : rs.length-1;
15631
15632         for(var i = start; i <= end; i++){
15633             v += (rs[i].data[property] || 0);
15634         }
15635         return v;
15636     },
15637
15638     /**
15639      * Filter the records by a specified property.
15640      * @param {String} field A field on your records
15641      * @param {String/RegExp} value Either a string that the field
15642      * should start with or a RegExp to test against the field
15643      * @param {Boolean} anyMatch True to match any part not just the beginning
15644      */
15645     filter : function(property, value, anyMatch){
15646         var fn = this.createFilterFn(property, value, anyMatch);
15647         return fn ? this.filterBy(fn) : this.clearFilter();
15648     },
15649
15650     /**
15651      * Filter by a function. The specified function will be called with each
15652      * record in this data source. If the function returns true the record is included,
15653      * otherwise it is filtered.
15654      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15655      * @param {Object} scope (optional) The scope of the function (defaults to this)
15656      */
15657     filterBy : function(fn, scope){
15658         this.snapshot = this.snapshot || this.data;
15659         this.data = this.queryBy(fn, scope||this);
15660         this.fireEvent("datachanged", this);
15661     },
15662
15663     /**
15664      * Query the records by a specified property.
15665      * @param {String} field A field on your records
15666      * @param {String/RegExp} value Either a string that the field
15667      * should start with or a RegExp to test against the field
15668      * @param {Boolean} anyMatch True to match any part not just the beginning
15669      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15670      */
15671     query : function(property, value, anyMatch){
15672         var fn = this.createFilterFn(property, value, anyMatch);
15673         return fn ? this.queryBy(fn) : this.data.clone();
15674     },
15675
15676     /**
15677      * Query by a function. The specified function will be called with each
15678      * record in this data source. If the function returns true the record is included
15679      * in the results.
15680      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15681      * @param {Object} scope (optional) The scope of the function (defaults to this)
15682       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15683      **/
15684     queryBy : function(fn, scope){
15685         var data = this.snapshot || this.data;
15686         return data.filterBy(fn, scope||this);
15687     },
15688
15689     /**
15690      * Collects unique values for a particular dataIndex from this store.
15691      * @param {String} dataIndex The property to collect
15692      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15693      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15694      * @return {Array} An array of the unique values
15695      **/
15696     collect : function(dataIndex, allowNull, bypassFilter){
15697         var d = (bypassFilter === true && this.snapshot) ?
15698                 this.snapshot.items : this.data.items;
15699         var v, sv, r = [], l = {};
15700         for(var i = 0, len = d.length; i < len; i++){
15701             v = d[i].data[dataIndex];
15702             sv = String(v);
15703             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15704                 l[sv] = true;
15705                 r[r.length] = v;
15706             }
15707         }
15708         return r;
15709     },
15710
15711     /**
15712      * Revert to a view of the Record cache with no filtering applied.
15713      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15714      */
15715     clearFilter : function(suppressEvent){
15716         if(this.snapshot && this.snapshot != this.data){
15717             this.data = this.snapshot;
15718             delete this.snapshot;
15719             if(suppressEvent !== true){
15720                 this.fireEvent("datachanged", this);
15721             }
15722         }
15723     },
15724
15725     // private
15726     afterEdit : function(record){
15727         if(this.modified.indexOf(record) == -1){
15728             this.modified.push(record);
15729         }
15730         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15731     },
15732     
15733     // private
15734     afterReject : function(record){
15735         this.modified.remove(record);
15736         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15737     },
15738
15739     // private
15740     afterCommit : function(record){
15741         this.modified.remove(record);
15742         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15743     },
15744
15745     /**
15746      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15747      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15748      */
15749     commitChanges : function(){
15750         var m = this.modified.slice(0);
15751         this.modified = [];
15752         for(var i = 0, len = m.length; i < len; i++){
15753             m[i].commit();
15754         }
15755     },
15756
15757     /**
15758      * Cancel outstanding changes on all changed records.
15759      */
15760     rejectChanges : function(){
15761         var m = this.modified.slice(0);
15762         this.modified = [];
15763         for(var i = 0, len = m.length; i < len; i++){
15764             m[i].reject();
15765         }
15766     },
15767
15768     onMetaChange : function(meta, rtype, o){
15769         this.recordType = rtype;
15770         this.fields = rtype.prototype.fields;
15771         delete this.snapshot;
15772         this.sortInfo = meta.sortInfo || this.sortInfo;
15773         this.modified = [];
15774         this.fireEvent('metachange', this, this.reader.meta);
15775     },
15776     
15777     moveIndex : function(data, type)
15778     {
15779         var index = this.indexOf(data);
15780         
15781         var newIndex = index + type;
15782         
15783         this.remove(data);
15784         
15785         this.insert(newIndex, data);
15786         
15787     }
15788 });/*
15789  * Based on:
15790  * Ext JS Library 1.1.1
15791  * Copyright(c) 2006-2007, Ext JS, LLC.
15792  *
15793  * Originally Released Under LGPL - original licence link has changed is not relivant.
15794  *
15795  * Fork - LGPL
15796  * <script type="text/javascript">
15797  */
15798
15799 /**
15800  * @class Roo.data.SimpleStore
15801  * @extends Roo.data.Store
15802  * Small helper class to make creating Stores from Array data easier.
15803  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15804  * @cfg {Array} fields An array of field definition objects, or field name strings.
15805  * @cfg {Object} an existing reader (eg. copied from another store)
15806  * @cfg {Array} data The multi-dimensional array of data
15807  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15808  * @cfg {Roo.data.Reader} reader  [not-required] 
15809  * @constructor
15810  * @param {Object} config
15811  */
15812 Roo.data.SimpleStore = function(config)
15813 {
15814     Roo.data.SimpleStore.superclass.constructor.call(this, {
15815         isLocal : true,
15816         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15817                 id: config.id
15818             },
15819             Roo.data.Record.create(config.fields)
15820         ),
15821         proxy : new Roo.data.MemoryProxy(config.data)
15822     });
15823     this.load();
15824 };
15825 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15826  * Based on:
15827  * Ext JS Library 1.1.1
15828  * Copyright(c) 2006-2007, Ext JS, LLC.
15829  *
15830  * Originally Released Under LGPL - original licence link has changed is not relivant.
15831  *
15832  * Fork - LGPL
15833  * <script type="text/javascript">
15834  */
15835
15836 /**
15837 /**
15838  * @extends Roo.data.Store
15839  * @class Roo.data.JsonStore
15840  * Small helper class to make creating Stores for JSON data easier. <br/>
15841 <pre><code>
15842 var store = new Roo.data.JsonStore({
15843     url: 'get-images.php',
15844     root: 'images',
15845     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15846 });
15847 </code></pre>
15848  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15849  * JsonReader and HttpProxy (unless inline data is provided).</b>
15850  * @cfg {Array} fields An array of field definition objects, or field name strings.
15851  * @constructor
15852  * @param {Object} config
15853  */
15854 Roo.data.JsonStore = function(c){
15855     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15856         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15857         reader: new Roo.data.JsonReader(c, c.fields)
15858     }));
15859 };
15860 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15861  * Based on:
15862  * Ext JS Library 1.1.1
15863  * Copyright(c) 2006-2007, Ext JS, LLC.
15864  *
15865  * Originally Released Under LGPL - original licence link has changed is not relivant.
15866  *
15867  * Fork - LGPL
15868  * <script type="text/javascript">
15869  */
15870
15871  
15872 Roo.data.Field = function(config){
15873     if(typeof config == "string"){
15874         config = {name: config};
15875     }
15876     Roo.apply(this, config);
15877     
15878     if(!this.type){
15879         this.type = "auto";
15880     }
15881     
15882     var st = Roo.data.SortTypes;
15883     // named sortTypes are supported, here we look them up
15884     if(typeof this.sortType == "string"){
15885         this.sortType = st[this.sortType];
15886     }
15887     
15888     // set default sortType for strings and dates
15889     if(!this.sortType){
15890         switch(this.type){
15891             case "string":
15892                 this.sortType = st.asUCString;
15893                 break;
15894             case "date":
15895                 this.sortType = st.asDate;
15896                 break;
15897             default:
15898                 this.sortType = st.none;
15899         }
15900     }
15901
15902     // define once
15903     var stripRe = /[\$,%]/g;
15904
15905     // prebuilt conversion function for this field, instead of
15906     // switching every time we're reading a value
15907     if(!this.convert){
15908         var cv, dateFormat = this.dateFormat;
15909         switch(this.type){
15910             case "":
15911             case "auto":
15912             case undefined:
15913                 cv = function(v){ return v; };
15914                 break;
15915             case "string":
15916                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15917                 break;
15918             case "int":
15919                 cv = function(v){
15920                     return v !== undefined && v !== null && v !== '' ?
15921                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15922                     };
15923                 break;
15924             case "float":
15925                 cv = function(v){
15926                     return v !== undefined && v !== null && v !== '' ?
15927                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15928                     };
15929                 break;
15930             case "bool":
15931             case "boolean":
15932                 cv = function(v){ return v === true || v === "true" || v == 1; };
15933                 break;
15934             case "date":
15935                 cv = function(v){
15936                     if(!v){
15937                         return '';
15938                     }
15939                     if(v instanceof Date){
15940                         return v;
15941                     }
15942                     if(dateFormat){
15943                         if(dateFormat == "timestamp"){
15944                             return new Date(v*1000);
15945                         }
15946                         return Date.parseDate(v, dateFormat);
15947                     }
15948                     var parsed = Date.parse(v);
15949                     return parsed ? new Date(parsed) : null;
15950                 };
15951              break;
15952             
15953         }
15954         this.convert = cv;
15955     }
15956 };
15957
15958 Roo.data.Field.prototype = {
15959     dateFormat: null,
15960     defaultValue: "",
15961     mapping: null,
15962     sortType : null,
15963     sortDir : "ASC"
15964 };/*
15965  * Based on:
15966  * Ext JS Library 1.1.1
15967  * Copyright(c) 2006-2007, Ext JS, LLC.
15968  *
15969  * Originally Released Under LGPL - original licence link has changed is not relivant.
15970  *
15971  * Fork - LGPL
15972  * <script type="text/javascript">
15973  */
15974  
15975 // Base class for reading structured data from a data source.  This class is intended to be
15976 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15977
15978 /**
15979  * @class Roo.data.DataReader
15980  * @abstract
15981  * Base class for reading structured data from a data source.  This class is intended to be
15982  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15983  */
15984
15985 Roo.data.DataReader = function(meta, recordType){
15986     
15987     this.meta = meta;
15988     
15989     this.recordType = recordType instanceof Array ? 
15990         Roo.data.Record.create(recordType) : recordType;
15991 };
15992
15993 Roo.data.DataReader.prototype = {
15994     
15995     
15996     readerType : 'Data',
15997      /**
15998      * Create an empty record
15999      * @param {Object} data (optional) - overlay some values
16000      * @return {Roo.data.Record} record created.
16001      */
16002     newRow :  function(d) {
16003         var da =  {};
16004         this.recordType.prototype.fields.each(function(c) {
16005             switch( c.type) {
16006                 case 'int' : da[c.name] = 0; break;
16007                 case 'date' : da[c.name] = new Date(); break;
16008                 case 'float' : da[c.name] = 0.0; break;
16009                 case 'boolean' : da[c.name] = false; break;
16010                 default : da[c.name] = ""; break;
16011             }
16012             
16013         });
16014         return new this.recordType(Roo.apply(da, d));
16015     }
16016     
16017     
16018 };/*
16019  * Based on:
16020  * Ext JS Library 1.1.1
16021  * Copyright(c) 2006-2007, Ext JS, LLC.
16022  *
16023  * Originally Released Under LGPL - original licence link has changed is not relivant.
16024  *
16025  * Fork - LGPL
16026  * <script type="text/javascript">
16027  */
16028
16029 /**
16030  * @class Roo.data.DataProxy
16031  * @extends Roo.util.Observable
16032  * @abstract
16033  * This class is an abstract base class for implementations which provide retrieval of
16034  * unformatted data objects.<br>
16035  * <p>
16036  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16037  * (of the appropriate type which knows how to parse the data object) to provide a block of
16038  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16039  * <p>
16040  * Custom implementations must implement the load method as described in
16041  * {@link Roo.data.HttpProxy#load}.
16042  */
16043 Roo.data.DataProxy = function(){
16044     this.addEvents({
16045         /**
16046          * @event beforeload
16047          * Fires before a network request is made to retrieve a data object.
16048          * @param {Object} This DataProxy object.
16049          * @param {Object} params The params parameter to the load function.
16050          */
16051         beforeload : true,
16052         /**
16053          * @event load
16054          * Fires before the load method's callback is called.
16055          * @param {Object} This DataProxy object.
16056          * @param {Object} o The data object.
16057          * @param {Object} arg The callback argument object passed to the load function.
16058          */
16059         load : true,
16060         /**
16061          * @event loadexception
16062          * Fires if an Exception occurs during data retrieval.
16063          * @param {Object} This DataProxy object.
16064          * @param {Object} o The data object.
16065          * @param {Object} arg The callback argument object passed to the load function.
16066          * @param {Object} e The Exception.
16067          */
16068         loadexception : true
16069     });
16070     Roo.data.DataProxy.superclass.constructor.call(this);
16071 };
16072
16073 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16074
16075     /**
16076      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16077      */
16078 /*
16079  * Based on:
16080  * Ext JS Library 1.1.1
16081  * Copyright(c) 2006-2007, Ext JS, LLC.
16082  *
16083  * Originally Released Under LGPL - original licence link has changed is not relivant.
16084  *
16085  * Fork - LGPL
16086  * <script type="text/javascript">
16087  */
16088 /**
16089  * @class Roo.data.MemoryProxy
16090  * @extends Roo.data.DataProxy
16091  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16092  * to the Reader when its load method is called.
16093  * @constructor
16094  * @param {Object} config  A config object containing the objects needed for the Store to access data,
16095  */
16096 Roo.data.MemoryProxy = function(config){
16097     var data = config;
16098     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16099         data = config.data;
16100     }
16101     Roo.data.MemoryProxy.superclass.constructor.call(this);
16102     this.data = data;
16103 };
16104
16105 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16106     
16107     /**
16108      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16109      */
16110     /**
16111      * Load data from the requested source (in this case an in-memory
16112      * data object passed to the constructor), read the data object into
16113      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16114      * process that block using the passed callback.
16115      * @param {Object} params This parameter is not used by the MemoryProxy class.
16116      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16117      * object into a block of Roo.data.Records.
16118      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16119      * The function must be passed <ul>
16120      * <li>The Record block object</li>
16121      * <li>The "arg" argument from the load function</li>
16122      * <li>A boolean success indicator</li>
16123      * </ul>
16124      * @param {Object} scope The scope in which to call the callback
16125      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16126      */
16127     load : function(params, reader, callback, scope, arg){
16128         params = params || {};
16129         var result;
16130         try {
16131             result = reader.readRecords(params.data ? params.data :this.data);
16132         }catch(e){
16133             this.fireEvent("loadexception", this, arg, null, e);
16134             callback.call(scope, null, arg, false);
16135             return;
16136         }
16137         callback.call(scope, result, arg, true);
16138     },
16139     
16140     // private
16141     update : function(params, records){
16142         
16143     }
16144 });/*
16145  * Based on:
16146  * Ext JS Library 1.1.1
16147  * Copyright(c) 2006-2007, Ext JS, LLC.
16148  *
16149  * Originally Released Under LGPL - original licence link has changed is not relivant.
16150  *
16151  * Fork - LGPL
16152  * <script type="text/javascript">
16153  */
16154 /**
16155  * @class Roo.data.HttpProxy
16156  * @extends Roo.data.DataProxy
16157  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16158  * configured to reference a certain URL.<br><br>
16159  * <p>
16160  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16161  * from which the running page was served.<br><br>
16162  * <p>
16163  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16164  * <p>
16165  * Be aware that to enable the browser to parse an XML document, the server must set
16166  * the Content-Type header in the HTTP response to "text/xml".
16167  * @constructor
16168  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16169  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16170  * will be used to make the request.
16171  */
16172 Roo.data.HttpProxy = function(conn){
16173     Roo.data.HttpProxy.superclass.constructor.call(this);
16174     // is conn a conn config or a real conn?
16175     this.conn = conn;
16176     this.useAjax = !conn || !conn.events;
16177   
16178 };
16179
16180 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16181     // thse are take from connection...
16182     
16183     /**
16184      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16185      */
16186     /**
16187      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16188      * extra parameters to each request made by this object. (defaults to undefined)
16189      */
16190     /**
16191      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16192      *  to each request made by this object. (defaults to undefined)
16193      */
16194     /**
16195      * @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)
16196      */
16197     /**
16198      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16199      */
16200      /**
16201      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16202      * @type Boolean
16203      */
16204   
16205
16206     /**
16207      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16208      * @type Boolean
16209      */
16210     /**
16211      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16212      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16213      * a finer-grained basis than the DataProxy events.
16214      */
16215     getConnection : function(){
16216         return this.useAjax ? Roo.Ajax : this.conn;
16217     },
16218
16219     /**
16220      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16221      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16222      * process that block using the passed callback.
16223      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16224      * for the request to the remote server.
16225      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16226      * object into a block of Roo.data.Records.
16227      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16228      * The function must be passed <ul>
16229      * <li>The Record block object</li>
16230      * <li>The "arg" argument from the load function</li>
16231      * <li>A boolean success indicator</li>
16232      * </ul>
16233      * @param {Object} scope The scope in which to call the callback
16234      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16235      */
16236     load : function(params, reader, callback, scope, arg){
16237         if(this.fireEvent("beforeload", this, params) !== false){
16238             var  o = {
16239                 params : params || {},
16240                 request: {
16241                     callback : callback,
16242                     scope : scope,
16243                     arg : arg
16244                 },
16245                 reader: reader,
16246                 callback : this.loadResponse,
16247                 scope: this
16248             };
16249             if(this.useAjax){
16250                 Roo.applyIf(o, this.conn);
16251                 if(this.activeRequest){
16252                     Roo.Ajax.abort(this.activeRequest);
16253                 }
16254                 this.activeRequest = Roo.Ajax.request(o);
16255             }else{
16256                 this.conn.request(o);
16257             }
16258         }else{
16259             callback.call(scope||this, null, arg, false);
16260         }
16261     },
16262
16263     // private
16264     loadResponse : function(o, success, response){
16265         delete this.activeRequest;
16266         if(!success){
16267             this.fireEvent("loadexception", this, o, response);
16268             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16269             return;
16270         }
16271         var result;
16272         try {
16273             result = o.reader.read(response);
16274         }catch(e){
16275             o.success = false;
16276             o.raw = { errorMsg : response.responseText };
16277             this.fireEvent("loadexception", this, o, response, e);
16278             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16279             return;
16280         }
16281         
16282         this.fireEvent("load", this, o, o.request.arg);
16283         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16284     },
16285
16286     // private
16287     update : function(dataSet){
16288
16289     },
16290
16291     // private
16292     updateResponse : function(dataSet){
16293
16294     }
16295 });/*
16296  * Based on:
16297  * Ext JS Library 1.1.1
16298  * Copyright(c) 2006-2007, Ext JS, LLC.
16299  *
16300  * Originally Released Under LGPL - original licence link has changed is not relivant.
16301  *
16302  * Fork - LGPL
16303  * <script type="text/javascript">
16304  */
16305
16306 /**
16307  * @class Roo.data.ScriptTagProxy
16308  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16309  * other than the originating domain of the running page.<br><br>
16310  * <p>
16311  * <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
16312  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16313  * <p>
16314  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16315  * source code that is used as the source inside a &lt;script> tag.<br><br>
16316  * <p>
16317  * In order for the browser to process the returned data, the server must wrap the data object
16318  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16319  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16320  * depending on whether the callback name was passed:
16321  * <p>
16322  * <pre><code>
16323 boolean scriptTag = false;
16324 String cb = request.getParameter("callback");
16325 if (cb != null) {
16326     scriptTag = true;
16327     response.setContentType("text/javascript");
16328 } else {
16329     response.setContentType("application/x-json");
16330 }
16331 Writer out = response.getWriter();
16332 if (scriptTag) {
16333     out.write(cb + "(");
16334 }
16335 out.print(dataBlock.toJsonString());
16336 if (scriptTag) {
16337     out.write(");");
16338 }
16339 </pre></code>
16340  *
16341  * @constructor
16342  * @param {Object} config A configuration object.
16343  */
16344 Roo.data.ScriptTagProxy = function(config){
16345     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16346     Roo.apply(this, config);
16347     this.head = document.getElementsByTagName("head")[0];
16348 };
16349
16350 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16351
16352 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16353     /**
16354      * @cfg {String} url The URL from which to request the data object.
16355      */
16356     /**
16357      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16358      */
16359     timeout : 30000,
16360     /**
16361      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16362      * the server the name of the callback function set up by the load call to process the returned data object.
16363      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16364      * javascript output which calls this named function passing the data object as its only parameter.
16365      */
16366     callbackParam : "callback",
16367     /**
16368      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16369      * name to the request.
16370      */
16371     nocache : true,
16372
16373     /**
16374      * Load data from the configured URL, read the data object into
16375      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16376      * process that block using the passed callback.
16377      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16378      * for the request to the remote server.
16379      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16380      * object into a block of Roo.data.Records.
16381      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16382      * The function must be passed <ul>
16383      * <li>The Record block object</li>
16384      * <li>The "arg" argument from the load function</li>
16385      * <li>A boolean success indicator</li>
16386      * </ul>
16387      * @param {Object} scope The scope in which to call the callback
16388      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16389      */
16390     load : function(params, reader, callback, scope, arg){
16391         if(this.fireEvent("beforeload", this, params) !== false){
16392
16393             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16394
16395             var url = this.url;
16396             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16397             if(this.nocache){
16398                 url += "&_dc=" + (new Date().getTime());
16399             }
16400             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16401             var trans = {
16402                 id : transId,
16403                 cb : "stcCallback"+transId,
16404                 scriptId : "stcScript"+transId,
16405                 params : params,
16406                 arg : arg,
16407                 url : url,
16408                 callback : callback,
16409                 scope : scope,
16410                 reader : reader
16411             };
16412             var conn = this;
16413
16414             window[trans.cb] = function(o){
16415                 conn.handleResponse(o, trans);
16416             };
16417
16418             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16419
16420             if(this.autoAbort !== false){
16421                 this.abort();
16422             }
16423
16424             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16425
16426             var script = document.createElement("script");
16427             script.setAttribute("src", url);
16428             script.setAttribute("type", "text/javascript");
16429             script.setAttribute("id", trans.scriptId);
16430             this.head.appendChild(script);
16431
16432             this.trans = trans;
16433         }else{
16434             callback.call(scope||this, null, arg, false);
16435         }
16436     },
16437
16438     // private
16439     isLoading : function(){
16440         return this.trans ? true : false;
16441     },
16442
16443     /**
16444      * Abort the current server request.
16445      */
16446     abort : function(){
16447         if(this.isLoading()){
16448             this.destroyTrans(this.trans);
16449         }
16450     },
16451
16452     // private
16453     destroyTrans : function(trans, isLoaded){
16454         this.head.removeChild(document.getElementById(trans.scriptId));
16455         clearTimeout(trans.timeoutId);
16456         if(isLoaded){
16457             window[trans.cb] = undefined;
16458             try{
16459                 delete window[trans.cb];
16460             }catch(e){}
16461         }else{
16462             // if hasn't been loaded, wait for load to remove it to prevent script error
16463             window[trans.cb] = function(){
16464                 window[trans.cb] = undefined;
16465                 try{
16466                     delete window[trans.cb];
16467                 }catch(e){}
16468             };
16469         }
16470     },
16471
16472     // private
16473     handleResponse : function(o, trans){
16474         this.trans = false;
16475         this.destroyTrans(trans, true);
16476         var result;
16477         try {
16478             result = trans.reader.readRecords(o);
16479         }catch(e){
16480             this.fireEvent("loadexception", this, o, trans.arg, e);
16481             trans.callback.call(trans.scope||window, null, trans.arg, false);
16482             return;
16483         }
16484         this.fireEvent("load", this, o, trans.arg);
16485         trans.callback.call(trans.scope||window, result, trans.arg, true);
16486     },
16487
16488     // private
16489     handleFailure : function(trans){
16490         this.trans = false;
16491         this.destroyTrans(trans, false);
16492         this.fireEvent("loadexception", this, null, trans.arg);
16493         trans.callback.call(trans.scope||window, null, trans.arg, false);
16494     }
16495 });/*
16496  * Based on:
16497  * Ext JS Library 1.1.1
16498  * Copyright(c) 2006-2007, Ext JS, LLC.
16499  *
16500  * Originally Released Under LGPL - original licence link has changed is not relivant.
16501  *
16502  * Fork - LGPL
16503  * <script type="text/javascript">
16504  */
16505
16506 /**
16507  * @class Roo.data.JsonReader
16508  * @extends Roo.data.DataReader
16509  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16510  * based on mappings in a provided Roo.data.Record constructor.
16511  * 
16512  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16513  * in the reply previously. 
16514  * 
16515  * <p>
16516  * Example code:
16517  * <pre><code>
16518 var RecordDef = Roo.data.Record.create([
16519     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16520     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16521 ]);
16522 var myReader = new Roo.data.JsonReader({
16523     totalProperty: "results",    // The property which contains the total dataset size (optional)
16524     root: "rows",                // The property which contains an Array of row objects
16525     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16526 }, RecordDef);
16527 </code></pre>
16528  * <p>
16529  * This would consume a JSON file like this:
16530  * <pre><code>
16531 { 'results': 2, 'rows': [
16532     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16533     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16534 }
16535 </code></pre>
16536  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16537  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16538  * paged from the remote server.
16539  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16540  * @cfg {String} root name of the property which contains the Array of row objects.
16541  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16542  * @cfg {Array} fields Array of field definition objects
16543  * @constructor
16544  * Create a new JsonReader
16545  * @param {Object} meta Metadata configuration options
16546  * @param {Object} recordType Either an Array of field definition objects,
16547  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16548  */
16549 Roo.data.JsonReader = function(meta, recordType){
16550     
16551     meta = meta || {};
16552     // set some defaults:
16553     Roo.applyIf(meta, {
16554         totalProperty: 'total',
16555         successProperty : 'success',
16556         root : 'data',
16557         id : 'id'
16558     });
16559     
16560     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16561 };
16562 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16563     
16564     readerType : 'Json',
16565     
16566     /**
16567      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16568      * Used by Store query builder to append _requestMeta to params.
16569      * 
16570      */
16571     metaFromRemote : false,
16572     /**
16573      * This method is only used by a DataProxy which has retrieved data from a remote server.
16574      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16575      * @return {Object} data A data block which is used by an Roo.data.Store object as
16576      * a cache of Roo.data.Records.
16577      */
16578     read : function(response){
16579         var json = response.responseText;
16580        
16581         var o = /* eval:var:o */ eval("("+json+")");
16582         if(!o) {
16583             throw {message: "JsonReader.read: Json object not found"};
16584         }
16585         
16586         if(o.metaData){
16587             
16588             delete this.ef;
16589             this.metaFromRemote = true;
16590             this.meta = o.metaData;
16591             this.recordType = Roo.data.Record.create(o.metaData.fields);
16592             this.onMetaChange(this.meta, this.recordType, o);
16593         }
16594         return this.readRecords(o);
16595     },
16596
16597     // private function a store will implement
16598     onMetaChange : function(meta, recordType, o){
16599
16600     },
16601
16602     /**
16603          * @ignore
16604          */
16605     simpleAccess: function(obj, subsc) {
16606         return obj[subsc];
16607     },
16608
16609         /**
16610          * @ignore
16611          */
16612     getJsonAccessor: function(){
16613         var re = /[\[\.]/;
16614         return function(expr) {
16615             try {
16616                 return(re.test(expr))
16617                     ? new Function("obj", "return obj." + expr)
16618                     : function(obj){
16619                         return obj[expr];
16620                     };
16621             } catch(e){}
16622             return Roo.emptyFn;
16623         };
16624     }(),
16625
16626     /**
16627      * Create a data block containing Roo.data.Records from an XML document.
16628      * @param {Object} o An object which contains an Array of row objects in the property specified
16629      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16630      * which contains the total size of the dataset.
16631      * @return {Object} data A data block which is used by an Roo.data.Store object as
16632      * a cache of Roo.data.Records.
16633      */
16634     readRecords : function(o){
16635         /**
16636          * After any data loads, the raw JSON data is available for further custom processing.
16637          * @type Object
16638          */
16639         this.o = o;
16640         var s = this.meta, Record = this.recordType,
16641             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16642
16643 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16644         if (!this.ef) {
16645             if(s.totalProperty) {
16646                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16647                 }
16648                 if(s.successProperty) {
16649                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16650                 }
16651                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16652                 if (s.id) {
16653                         var g = this.getJsonAccessor(s.id);
16654                         this.getId = function(rec) {
16655                                 var r = g(rec);  
16656                                 return (r === undefined || r === "") ? null : r;
16657                         };
16658                 } else {
16659                         this.getId = function(){return null;};
16660                 }
16661             this.ef = [];
16662             for(var jj = 0; jj < fl; jj++){
16663                 f = fi[jj];
16664                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16665                 this.ef[jj] = this.getJsonAccessor(map);
16666             }
16667         }
16668
16669         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16670         if(s.totalProperty){
16671             var vt = parseInt(this.getTotal(o), 10);
16672             if(!isNaN(vt)){
16673                 totalRecords = vt;
16674             }
16675         }
16676         if(s.successProperty){
16677             var vs = this.getSuccess(o);
16678             if(vs === false || vs === 'false'){
16679                 success = false;
16680             }
16681         }
16682         var records = [];
16683         for(var i = 0; i < c; i++){
16684             var n = root[i];
16685             var values = {};
16686             var id = this.getId(n);
16687             for(var j = 0; j < fl; j++){
16688                 f = fi[j];
16689                                 var v = this.ef[j](n);
16690                                 if (!f.convert) {
16691                                         Roo.log('missing convert for ' + f.name);
16692                                         Roo.log(f);
16693                                         continue;
16694                                 }
16695                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16696             }
16697                         if (!Record) {
16698                                 return {
16699                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16700                                         success : false,
16701                                         records : [],
16702                                         totalRecords : 0
16703                                 };
16704                         }
16705             var record = new Record(values, id);
16706             record.json = n;
16707             records[i] = record;
16708         }
16709         return {
16710             raw : o,
16711             success : success,
16712             records : records,
16713             totalRecords : totalRecords
16714         };
16715     },
16716     // used when loading children.. @see loadDataFromChildren
16717     toLoadData: function(rec)
16718     {
16719         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16720         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16721         return { data : data, total : data.length };
16722         
16723     }
16724 });/*
16725  * Based on:
16726  * Ext JS Library 1.1.1
16727  * Copyright(c) 2006-2007, Ext JS, LLC.
16728  *
16729  * Originally Released Under LGPL - original licence link has changed is not relivant.
16730  *
16731  * Fork - LGPL
16732  * <script type="text/javascript">
16733  */
16734
16735 /**
16736  * @class Roo.data.ArrayReader
16737  * @extends Roo.data.DataReader
16738  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16739  * Each element of that Array represents a row of data fields. The
16740  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16741  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16742  * <p>
16743  * Example code:.
16744  * <pre><code>
16745 var RecordDef = Roo.data.Record.create([
16746     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16747     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16748 ]);
16749 var myReader = new Roo.data.ArrayReader({
16750     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16751 }, RecordDef);
16752 </code></pre>
16753  * <p>
16754  * This would consume an Array like this:
16755  * <pre><code>
16756 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16757   </code></pre>
16758  
16759  * @constructor
16760  * Create a new JsonReader
16761  * @param {Object} meta Metadata configuration options.
16762  * @param {Object|Array} recordType Either an Array of field definition objects
16763  * 
16764  * @cfg {Array} fields Array of field definition objects
16765  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16766  * as specified to {@link Roo.data.Record#create},
16767  * or an {@link Roo.data.Record} object
16768  *
16769  * 
16770  * created using {@link Roo.data.Record#create}.
16771  */
16772 Roo.data.ArrayReader = function(meta, recordType)
16773 {    
16774     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16775 };
16776
16777 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16778     
16779       /**
16780      * Create a data block containing Roo.data.Records from an XML document.
16781      * @param {Object} o An Array of row objects which represents the dataset.
16782      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16783      * a cache of Roo.data.Records.
16784      */
16785     readRecords : function(o)
16786     {
16787         var sid = this.meta ? this.meta.id : null;
16788         var recordType = this.recordType, fields = recordType.prototype.fields;
16789         var records = [];
16790         var root = o;
16791         for(var i = 0; i < root.length; i++){
16792             var n = root[i];
16793             var values = {};
16794             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16795             for(var j = 0, jlen = fields.length; j < jlen; j++){
16796                 var f = fields.items[j];
16797                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16798                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16799                 v = f.convert(v);
16800                 values[f.name] = v;
16801             }
16802             var record = new recordType(values, id);
16803             record.json = n;
16804             records[records.length] = record;
16805         }
16806         return {
16807             records : records,
16808             totalRecords : records.length
16809         };
16810     },
16811     // used when loading children.. @see loadDataFromChildren
16812     toLoadData: function(rec)
16813     {
16814         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16815         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16816         
16817     }
16818     
16819     
16820 });/*
16821  * - LGPL
16822  * * 
16823  */
16824
16825 /**
16826  * @class Roo.bootstrap.form.ComboBox
16827  * @extends Roo.bootstrap.form.TriggerField
16828  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16829  * @cfg {Boolean} append (true|false) default false
16830  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16831  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16832  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16833  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16834  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16835  * @cfg {Boolean} animate default true
16836  * @cfg {Boolean} emptyResultText only for touch device
16837  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16838  * @cfg {String} emptyTitle default ''
16839  * @cfg {Number} width fixed with? experimental
16840  * @constructor
16841  * Create a new ComboBox.
16842  * @param {Object} config Configuration options
16843  */
16844 Roo.bootstrap.form.ComboBox = function(config){
16845     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16846     this.addEvents({
16847         /**
16848          * @event expand
16849          * Fires when the dropdown list is expanded
16850         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16851         */
16852         'expand' : true,
16853         /**
16854          * @event collapse
16855          * Fires when the dropdown list is collapsed
16856         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16857         */
16858         'collapse' : true,
16859         /**
16860          * @event beforeselect
16861          * Fires before a list item is selected. Return false to cancel the selection.
16862         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16863         * @param {Roo.data.Record} record The data record returned from the underlying store
16864         * @param {Number} index The index of the selected item in the dropdown list
16865         */
16866         'beforeselect' : true,
16867         /**
16868          * @event select
16869          * Fires when a list item is selected
16870         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16871         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16872         * @param {Number} index The index of the selected item in the dropdown list
16873         */
16874         'select' : true,
16875         /**
16876          * @event beforequery
16877          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16878          * The event object passed has these properties:
16879         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16880         * @param {String} query The query
16881         * @param {Boolean} forceAll true to force "all" query
16882         * @param {Boolean} cancel true to cancel the query
16883         * @param {Object} e The query event object
16884         */
16885         'beforequery': true,
16886          /**
16887          * @event add
16888          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16889         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16890         */
16891         'add' : true,
16892         /**
16893          * @event edit
16894          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16895         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16896         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16897         */
16898         'edit' : true,
16899         /**
16900          * @event remove
16901          * Fires when the remove value from the combobox array
16902         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16903         */
16904         'remove' : true,
16905         /**
16906          * @event afterremove
16907          * Fires when the remove value from the combobox array
16908         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16909         */
16910         'afterremove' : true,
16911         /**
16912          * @event specialfilter
16913          * Fires when specialfilter
16914             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16915             */
16916         'specialfilter' : true,
16917         /**
16918          * @event tick
16919          * Fires when tick the element
16920             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16921             */
16922         'tick' : true,
16923         /**
16924          * @event touchviewdisplay
16925          * Fires when touch view require special display (default is using displayField)
16926             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16927             * @param {Object} cfg set html .
16928             */
16929         'touchviewdisplay' : true
16930         
16931     });
16932     
16933     this.item = [];
16934     this.tickItems = [];
16935     
16936     this.selectedIndex = -1;
16937     if(this.mode == 'local'){
16938         if(config.queryDelay === undefined){
16939             this.queryDelay = 10;
16940         }
16941         if(config.minChars === undefined){
16942             this.minChars = 0;
16943         }
16944     }
16945 };
16946
16947 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16948      
16949     /**
16950      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16951      * rendering into an Roo.Editor, defaults to false)
16952      */
16953     /**
16954      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16955      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16956      */
16957     /**
16958      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16959      */
16960     /**
16961      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16962      * the dropdown list (defaults to undefined, with no header element)
16963      */
16964
16965      /**
16966      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16967      */
16968      
16969      /**
16970      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16971      */
16972     listWidth: undefined,
16973     /**
16974      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16975      * mode = 'remote' or 'text' if mode = 'local')
16976      */
16977     displayField: undefined,
16978     
16979     /**
16980      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16981      * mode = 'remote' or 'value' if mode = 'local'). 
16982      * Note: use of a valueField requires the user make a selection
16983      * in order for a value to be mapped.
16984      */
16985     valueField: undefined,
16986     /**
16987      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16988      */
16989     modalTitle : '',
16990     
16991     /**
16992      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16993      * field's data value (defaults to the underlying DOM element's name)
16994      */
16995     hiddenName: undefined,
16996     /**
16997      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16998      */
16999     listClass: '',
17000     /**
17001      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17002      */
17003     selectedClass: 'active',
17004     
17005     /**
17006      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17007      */
17008     shadow:'sides',
17009     /**
17010      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17011      * anchor positions (defaults to 'tl-bl')
17012      */
17013     listAlign: 'tl-bl?',
17014     /**
17015      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17016      */
17017     maxHeight: 300,
17018     /**
17019      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17020      * query specified by the allQuery config option (defaults to 'query')
17021      */
17022     triggerAction: 'query',
17023     /**
17024      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17025      * (defaults to 4, does not apply if editable = false)
17026      */
17027     minChars : 4,
17028     /**
17029      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17030      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17031      */
17032     typeAhead: false,
17033     /**
17034      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17035      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17036      */
17037     queryDelay: 500,
17038     /**
17039      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17040      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17041      */
17042     pageSize: 0,
17043     /**
17044      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17045      * when editable = true (defaults to false)
17046      */
17047     selectOnFocus:false,
17048     /**
17049      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17050      */
17051     queryParam: 'query',
17052     /**
17053      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17054      * when mode = 'remote' (defaults to 'Loading...')
17055      */
17056     loadingText: 'Loading...',
17057     /**
17058      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17059      */
17060     resizable: false,
17061     /**
17062      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17063      */
17064     handleHeight : 8,
17065     /**
17066      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17067      * traditional select (defaults to true)
17068      */
17069     editable: true,
17070     /**
17071      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17072      */
17073     allQuery: '',
17074     /**
17075      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17076      */
17077     mode: 'remote',
17078     /**
17079      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17080      * listWidth has a higher value)
17081      */
17082     minListWidth : 70,
17083     /**
17084      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17085      * allow the user to set arbitrary text into the field (defaults to false)
17086      */
17087     forceSelection:false,
17088     /**
17089      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17090      * if typeAhead = true (defaults to 250)
17091      */
17092     typeAheadDelay : 250,
17093     /**
17094      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17095      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17096      */
17097     valueNotFoundText : undefined,
17098     /**
17099      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17100      */
17101     blockFocus : false,
17102     
17103     /**
17104      * @cfg {Boolean} disableClear Disable showing of clear button.
17105      */
17106     disableClear : false,
17107     /**
17108      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17109      */
17110     alwaysQuery : false,
17111     
17112     /**
17113      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17114      */
17115     multiple : false,
17116     
17117     /**
17118      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17119      */
17120     invalidClass : "has-warning",
17121     
17122     /**
17123      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17124      */
17125     validClass : "has-success",
17126     
17127     /**
17128      * @cfg {Boolean} specialFilter (true|false) special filter default false
17129      */
17130     specialFilter : false,
17131     
17132     /**
17133      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17134      */
17135     mobileTouchView : true,
17136     
17137     /**
17138      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17139      */
17140     useNativeIOS : false,
17141     
17142     /**
17143      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17144      */
17145     mobile_restrict_height : false,
17146     
17147     ios_options : false,
17148     
17149     //private
17150     addicon : false,
17151     editicon: false,
17152     
17153     page: 0,
17154     hasQuery: false,
17155     append: false,
17156     loadNext: false,
17157     autoFocus : true,
17158     tickable : false,
17159     btnPosition : 'right',
17160     triggerList : true,
17161     showToggleBtn : true,
17162     animate : true,
17163     emptyResultText: 'Empty',
17164     triggerText : 'Select',
17165     emptyTitle : '',
17166     width : false,
17167     
17168     // element that contains real text value.. (when hidden is used..)
17169     
17170     getAutoCreate : function()
17171     {   
17172         var cfg = false;
17173         //render
17174         /*
17175          * Render classic select for iso
17176          */
17177         
17178         if(Roo.isIOS && this.useNativeIOS){
17179             cfg = this.getAutoCreateNativeIOS();
17180             return cfg;
17181         }
17182         
17183         /*
17184          * Touch Devices
17185          */
17186         
17187         if(Roo.isTouch && this.mobileTouchView){
17188             cfg = this.getAutoCreateTouchView();
17189             return cfg;;
17190         }
17191         
17192         /*
17193          *  Normal ComboBox
17194          */
17195         if(!this.tickable){
17196             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17197             return cfg;
17198         }
17199         
17200         /*
17201          *  ComboBox with tickable selections
17202          */
17203              
17204         var align = this.labelAlign || this.parentLabelAlign();
17205         
17206         cfg = {
17207             cls : 'form-group roo-combobox-tickable' //input-group
17208         };
17209         
17210         var btn_text_select = '';
17211         var btn_text_done = '';
17212         var btn_text_cancel = '';
17213         
17214         if (this.btn_text_show) {
17215             btn_text_select = 'Select';
17216             btn_text_done = 'Done';
17217             btn_text_cancel = 'Cancel'; 
17218         }
17219         
17220         var buttons = {
17221             tag : 'div',
17222             cls : 'tickable-buttons',
17223             cn : [
17224                 {
17225                     tag : 'button',
17226                     type : 'button',
17227                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17228                     //html : this.triggerText
17229                     html: btn_text_select
17230                 },
17231                 {
17232                     tag : 'button',
17233                     type : 'button',
17234                     name : 'ok',
17235                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17236                     //html : 'Done'
17237                     html: btn_text_done
17238                 },
17239                 {
17240                     tag : 'button',
17241                     type : 'button',
17242                     name : 'cancel',
17243                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17244                     //html : 'Cancel'
17245                     html: btn_text_cancel
17246                 }
17247             ]
17248         };
17249         
17250         if(this.editable){
17251             buttons.cn.unshift({
17252                 tag: 'input',
17253                 cls: 'roo-select2-search-field-input'
17254             });
17255         }
17256         
17257         var _this = this;
17258         
17259         Roo.each(buttons.cn, function(c){
17260             if (_this.size) {
17261                 c.cls += ' btn-' + _this.size;
17262             }
17263
17264             if (_this.disabled) {
17265                 c.disabled = true;
17266             }
17267         });
17268         
17269         var box = {
17270             tag: 'div',
17271             style : 'display: contents',
17272             cn: [
17273                 {
17274                     tag: 'input',
17275                     type : 'hidden',
17276                     cls: 'form-hidden-field'
17277                 },
17278                 {
17279                     tag: 'ul',
17280                     cls: 'roo-select2-choices',
17281                     cn:[
17282                         {
17283                             tag: 'li',
17284                             cls: 'roo-select2-search-field',
17285                             cn: [
17286                                 buttons
17287                             ]
17288                         }
17289                     ]
17290                 }
17291             ]
17292         };
17293         
17294         var combobox = {
17295             cls: 'roo-select2-container input-group roo-select2-container-multi',
17296             cn: [
17297                 
17298                 box
17299 //                {
17300 //                    tag: 'ul',
17301 //                    cls: 'typeahead typeahead-long dropdown-menu',
17302 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17303 //                }
17304             ]
17305         };
17306         
17307         if(this.hasFeedback && !this.allowBlank){
17308             
17309             var feedback = {
17310                 tag: 'span',
17311                 cls: 'glyphicon form-control-feedback'
17312             };
17313
17314             combobox.cn.push(feedback);
17315         }
17316         
17317         
17318         
17319         var indicator = {
17320             tag : 'i',
17321             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17322             tooltip : 'This field is required'
17323         };
17324         if (Roo.bootstrap.version == 4) {
17325             indicator = {
17326                 tag : 'i',
17327                 style : 'display:none'
17328             };
17329         }
17330         if (align ==='left' && this.fieldLabel.length) {
17331             
17332             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17333             
17334             cfg.cn = [
17335                 indicator,
17336                 {
17337                     tag: 'label',
17338                     'for' :  id,
17339                     cls : 'control-label col-form-label',
17340                     html : this.fieldLabel
17341
17342                 },
17343                 {
17344                     cls : "", 
17345                     cn: [
17346                         combobox
17347                     ]
17348                 }
17349
17350             ];
17351             
17352             var labelCfg = cfg.cn[1];
17353             var contentCfg = cfg.cn[2];
17354             
17355
17356             if(this.indicatorpos == 'right'){
17357                 
17358                 cfg.cn = [
17359                     {
17360                         tag: 'label',
17361                         'for' :  id,
17362                         cls : 'control-label col-form-label',
17363                         cn : [
17364                             {
17365                                 tag : 'span',
17366                                 html : this.fieldLabel
17367                             },
17368                             indicator
17369                         ]
17370                     },
17371                     {
17372                         cls : "",
17373                         cn: [
17374                             combobox
17375                         ]
17376                     }
17377
17378                 ];
17379                 
17380                 
17381                 
17382                 labelCfg = cfg.cn[0];
17383                 contentCfg = cfg.cn[1];
17384             
17385             }
17386             
17387             if(this.labelWidth > 12){
17388                 labelCfg.style = "width: " + this.labelWidth + 'px';
17389             }
17390             if(this.width * 1 > 0){
17391                 contentCfg.style = "width: " + this.width + 'px';
17392             }
17393             if(this.labelWidth < 13 && this.labelmd == 0){
17394                 this.labelmd = this.labelWidth;
17395             }
17396             
17397             if(this.labellg > 0){
17398                 labelCfg.cls += ' col-lg-' + this.labellg;
17399                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17400             }
17401             
17402             if(this.labelmd > 0){
17403                 labelCfg.cls += ' col-md-' + this.labelmd;
17404                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17405             }
17406             
17407             if(this.labelsm > 0){
17408                 labelCfg.cls += ' col-sm-' + this.labelsm;
17409                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17410             }
17411             
17412             if(this.labelxs > 0){
17413                 labelCfg.cls += ' col-xs-' + this.labelxs;
17414                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17415             }
17416                 
17417                 
17418         } else if ( this.fieldLabel.length) {
17419 //                Roo.log(" label");
17420                  cfg.cn = [
17421                    indicator,
17422                     {
17423                         tag: 'label',
17424                         //cls : 'input-group-addon',
17425                         html : this.fieldLabel
17426                     },
17427                     combobox
17428                 ];
17429                 
17430                 if(this.indicatorpos == 'right'){
17431                     cfg.cn = [
17432                         {
17433                             tag: 'label',
17434                             //cls : 'input-group-addon',
17435                             html : this.fieldLabel
17436                         },
17437                         indicator,
17438                         combobox
17439                     ];
17440                     
17441                 }
17442
17443         } else {
17444             
17445 //                Roo.log(" no label && no align");
17446                 cfg = combobox
17447                      
17448                 
17449         }
17450          
17451         var settings=this;
17452         ['xs','sm','md','lg'].map(function(size){
17453             if (settings[size]) {
17454                 cfg.cls += ' col-' + size + '-' + settings[size];
17455             }
17456         });
17457         
17458         return cfg;
17459         
17460     },
17461     
17462     _initEventsCalled : false,
17463     
17464     // private
17465     initEvents: function()
17466     {   
17467         if (this._initEventsCalled) { // as we call render... prevent looping...
17468             return;
17469         }
17470         this._initEventsCalled = true;
17471         
17472         if (!this.store) {
17473             throw "can not find store for combo";
17474         }
17475         
17476         this.indicator = this.indicatorEl();
17477         
17478         this.store = Roo.factory(this.store, Roo.data);
17479         this.store.parent = this;
17480         
17481         // if we are building from html. then this element is so complex, that we can not really
17482         // use the rendered HTML.
17483         // so we have to trash and replace the previous code.
17484         if (Roo.XComponent.build_from_html) {
17485             // remove this element....
17486             var e = this.el.dom, k=0;
17487             while (e ) { e = e.previousSibling;  ++k;}
17488
17489             this.el.remove();
17490             
17491             this.el=false;
17492             this.rendered = false;
17493             
17494             this.render(this.parent().getChildContainer(true), k);
17495         }
17496         
17497         if(Roo.isIOS && this.useNativeIOS){
17498             this.initIOSView();
17499             return;
17500         }
17501         
17502         /*
17503          * Touch Devices
17504          */
17505         
17506         if(Roo.isTouch && this.mobileTouchView){
17507             this.initTouchView();
17508             return;
17509         }
17510         
17511         if(this.tickable){
17512             this.initTickableEvents();
17513             return;
17514         }
17515         
17516         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17517         
17518         if(this.hiddenName){
17519             
17520             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17521             
17522             this.hiddenField.dom.value =
17523                 this.hiddenValue !== undefined ? this.hiddenValue :
17524                 this.value !== undefined ? this.value : '';
17525
17526             // prevent input submission
17527             this.el.dom.removeAttribute('name');
17528             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17529              
17530              
17531         }
17532         //if(Roo.isGecko){
17533         //    this.el.dom.setAttribute('autocomplete', 'off');
17534         //}
17535         
17536         var cls = 'x-combo-list';
17537         
17538         //this.list = new Roo.Layer({
17539         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17540         //});
17541         
17542         var _this = this;
17543         
17544         (function(){
17545             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17546             _this.list.setWidth(lw);
17547         }).defer(100);
17548         
17549         this.list.on('mouseover', this.onViewOver, this);
17550         this.list.on('mousemove', this.onViewMove, this);
17551         this.list.on('scroll', this.onViewScroll, this);
17552         
17553         /*
17554         this.list.swallowEvent('mousewheel');
17555         this.assetHeight = 0;
17556
17557         if(this.title){
17558             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17559             this.assetHeight += this.header.getHeight();
17560         }
17561
17562         this.innerList = this.list.createChild({cls:cls+'-inner'});
17563         this.innerList.on('mouseover', this.onViewOver, this);
17564         this.innerList.on('mousemove', this.onViewMove, this);
17565         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17566         
17567         if(this.allowBlank && !this.pageSize && !this.disableClear){
17568             this.footer = this.list.createChild({cls:cls+'-ft'});
17569             this.pageTb = new Roo.Toolbar(this.footer);
17570            
17571         }
17572         if(this.pageSize){
17573             this.footer = this.list.createChild({cls:cls+'-ft'});
17574             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17575                     {pageSize: this.pageSize});
17576             
17577         }
17578         
17579         if (this.pageTb && this.allowBlank && !this.disableClear) {
17580             var _this = this;
17581             this.pageTb.add(new Roo.Toolbar.Fill(), {
17582                 cls: 'x-btn-icon x-btn-clear',
17583                 text: '&#160;',
17584                 handler: function()
17585                 {
17586                     _this.collapse();
17587                     _this.clearValue();
17588                     _this.onSelect(false, -1);
17589                 }
17590             });
17591         }
17592         if (this.footer) {
17593             this.assetHeight += this.footer.getHeight();
17594         }
17595         */
17596             
17597         if(!this.tpl){
17598             this.tpl = Roo.bootstrap.version == 4 ?
17599                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17600                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17601         }
17602
17603         this.view = new Roo.View(this.list, this.tpl, {
17604             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17605         });
17606         //this.view.wrapEl.setDisplayed(false);
17607         this.view.on('click', this.onViewClick, this);
17608         
17609         
17610         this.store.on('beforeload', this.onBeforeLoad, this);
17611         this.store.on('load', this.onLoad, this);
17612         this.store.on('loadexception', this.onLoadException, this);
17613         /*
17614         if(this.resizable){
17615             this.resizer = new Roo.Resizable(this.list,  {
17616                pinned:true, handles:'se'
17617             });
17618             this.resizer.on('resize', function(r, w, h){
17619                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17620                 this.listWidth = w;
17621                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17622                 this.restrictHeight();
17623             }, this);
17624             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17625         }
17626         */
17627         if(!this.editable){
17628             this.editable = true;
17629             this.setEditable(false);
17630         }
17631         
17632         /*
17633         
17634         if (typeof(this.events.add.listeners) != 'undefined') {
17635             
17636             this.addicon = this.wrap.createChild(
17637                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17638        
17639             this.addicon.on('click', function(e) {
17640                 this.fireEvent('add', this);
17641             }, this);
17642         }
17643         if (typeof(this.events.edit.listeners) != 'undefined') {
17644             
17645             this.editicon = this.wrap.createChild(
17646                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17647             if (this.addicon) {
17648                 this.editicon.setStyle('margin-left', '40px');
17649             }
17650             this.editicon.on('click', function(e) {
17651                 
17652                 // we fire even  if inothing is selected..
17653                 this.fireEvent('edit', this, this.lastData );
17654                 
17655             }, this);
17656         }
17657         */
17658         
17659         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17660             "up" : function(e){
17661                 this.inKeyMode = true;
17662                 this.selectPrev();
17663             },
17664
17665             "down" : function(e){
17666                 if(!this.isExpanded()){
17667                     this.onTriggerClick();
17668                 }else{
17669                     this.inKeyMode = true;
17670                     this.selectNext();
17671                 }
17672             },
17673
17674             "enter" : function(e){
17675 //                this.onViewClick();
17676                 //return true;
17677                 this.collapse();
17678                 
17679                 if(this.fireEvent("specialkey", this, e)){
17680                     this.onViewClick(false);
17681                 }
17682                 
17683                 return true;
17684             },
17685
17686             "esc" : function(e){
17687                 this.collapse();
17688             },
17689
17690             "tab" : function(e){
17691                 this.collapse();
17692                 
17693                 if(this.fireEvent("specialkey", this, e)){
17694                     this.onViewClick(false);
17695                 }
17696                 
17697                 return true;
17698             },
17699
17700             scope : this,
17701
17702             doRelay : function(foo, bar, hname){
17703                 if(hname == 'down' || this.scope.isExpanded()){
17704                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17705                 }
17706                 return true;
17707             },
17708
17709             forceKeyDown: true
17710         });
17711         
17712         
17713         this.queryDelay = Math.max(this.queryDelay || 10,
17714                 this.mode == 'local' ? 10 : 250);
17715         
17716         
17717         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17718         
17719         if(this.typeAhead){
17720             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17721         }
17722         if(this.editable !== false){
17723             this.inputEl().on("keyup", this.onKeyUp, this);
17724         }
17725         if(this.forceSelection){
17726             this.inputEl().on('blur', this.doForce, this);
17727         }
17728         
17729         if(this.multiple){
17730             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17731             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17732         }
17733     },
17734     
17735     initTickableEvents: function()
17736     {   
17737         this.createList();
17738         
17739         if(this.hiddenName){
17740             
17741             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17742             
17743             this.hiddenField.dom.value =
17744                 this.hiddenValue !== undefined ? this.hiddenValue :
17745                 this.value !== undefined ? this.value : '';
17746
17747             // prevent input submission
17748             this.el.dom.removeAttribute('name');
17749             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17750              
17751              
17752         }
17753         
17754 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17755         
17756         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17757         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17758         if(this.triggerList){
17759             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17760         }
17761          
17762         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17763         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17764         
17765         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17766         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17767         
17768         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17769         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17770         
17771         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17772         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17773         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17774         
17775         this.okBtn.hide();
17776         this.cancelBtn.hide();
17777         
17778         var _this = this;
17779         
17780         (function(){
17781             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17782             _this.list.setWidth(lw);
17783         }).defer(100);
17784         
17785         this.list.on('mouseover', this.onViewOver, this);
17786         this.list.on('mousemove', this.onViewMove, this);
17787         
17788         this.list.on('scroll', this.onViewScroll, this);
17789         
17790         if(!this.tpl){
17791             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17792                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17793         }
17794
17795         this.view = new Roo.View(this.list, this.tpl, {
17796             singleSelect:true,
17797             tickable:true,
17798             parent:this,
17799             store: this.store,
17800             selectedClass: this.selectedClass
17801         });
17802         
17803         //this.view.wrapEl.setDisplayed(false);
17804         this.view.on('click', this.onViewClick, this);
17805         
17806         
17807         
17808         this.store.on('beforeload', this.onBeforeLoad, this);
17809         this.store.on('load', this.onLoad, this);
17810         this.store.on('loadexception', this.onLoadException, this);
17811         
17812         if(this.editable){
17813             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17814                 "up" : function(e){
17815                     this.inKeyMode = true;
17816                     this.selectPrev();
17817                 },
17818
17819                 "down" : function(e){
17820                     this.inKeyMode = true;
17821                     this.selectNext();
17822                 },
17823
17824                 "enter" : function(e){
17825                     if(this.fireEvent("specialkey", this, e)){
17826                         this.onViewClick(false);
17827                     }
17828                     
17829                     return true;
17830                 },
17831
17832                 "esc" : function(e){
17833                     this.onTickableFooterButtonClick(e, false, false);
17834                 },
17835
17836                 "tab" : function(e){
17837                     this.fireEvent("specialkey", this, e);
17838                     
17839                     this.onTickableFooterButtonClick(e, false, false);
17840                     
17841                     return true;
17842                 },
17843
17844                 scope : this,
17845
17846                 doRelay : function(e, fn, key){
17847                     if(this.scope.isExpanded()){
17848                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17849                     }
17850                     return true;
17851                 },
17852
17853                 forceKeyDown: true
17854             });
17855         }
17856         
17857         this.queryDelay = Math.max(this.queryDelay || 10,
17858                 this.mode == 'local' ? 10 : 250);
17859         
17860         
17861         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17862         
17863         if(this.typeAhead){
17864             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17865         }
17866         
17867         if(this.editable !== false){
17868             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17869         }
17870         
17871         this.indicator = this.indicatorEl();
17872         
17873         if(this.indicator){
17874             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17875             this.indicator.hide();
17876         }
17877         
17878     },
17879
17880     onDestroy : function(){
17881         if(this.view){
17882             this.view.setStore(null);
17883             this.view.el.removeAllListeners();
17884             this.view.el.remove();
17885             this.view.purgeListeners();
17886         }
17887         if(this.list){
17888             this.list.dom.innerHTML  = '';
17889         }
17890         
17891         if(this.store){
17892             this.store.un('beforeload', this.onBeforeLoad, this);
17893             this.store.un('load', this.onLoad, this);
17894             this.store.un('loadexception', this.onLoadException, this);
17895         }
17896         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17897     },
17898
17899     // private
17900     fireKey : function(e){
17901         if(e.isNavKeyPress() && !this.list.isVisible()){
17902             this.fireEvent("specialkey", this, e);
17903         }
17904     },
17905
17906     // private
17907     onResize: function(w, h)
17908     {
17909         
17910         
17911 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17912 //        
17913 //        if(typeof w != 'number'){
17914 //            // we do not handle it!?!?
17915 //            return;
17916 //        }
17917 //        var tw = this.trigger.getWidth();
17918 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17919 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17920 //        var x = w - tw;
17921 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17922 //            
17923 //        //this.trigger.setStyle('left', x+'px');
17924 //        
17925 //        if(this.list && this.listWidth === undefined){
17926 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17927 //            this.list.setWidth(lw);
17928 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17929 //        }
17930         
17931     
17932         
17933     },
17934
17935     /**
17936      * Allow or prevent the user from directly editing the field text.  If false is passed,
17937      * the user will only be able to select from the items defined in the dropdown list.  This method
17938      * is the runtime equivalent of setting the 'editable' config option at config time.
17939      * @param {Boolean} value True to allow the user to directly edit the field text
17940      */
17941     setEditable : function(value){
17942         if(value == this.editable){
17943             return;
17944         }
17945         this.editable = value;
17946         if(!value){
17947             this.inputEl().dom.setAttribute('readOnly', true);
17948             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17949             this.inputEl().addClass('x-combo-noedit');
17950         }else{
17951             this.inputEl().dom.removeAttribute('readOnly');
17952             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17953             this.inputEl().removeClass('x-combo-noedit');
17954         }
17955     },
17956
17957     // private
17958     
17959     onBeforeLoad : function(combo,opts){
17960         if(!this.hasFocus){
17961             return;
17962         }
17963          if (!opts.add) {
17964             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17965          }
17966         this.restrictHeight();
17967         this.selectedIndex = -1;
17968     },
17969
17970     // private
17971     onLoad : function(){
17972         
17973         this.hasQuery = false;
17974         
17975         if(!this.hasFocus){
17976             return;
17977         }
17978         
17979         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17980             this.loading.hide();
17981         }
17982         
17983         if(this.store.getCount() > 0){
17984             
17985             this.expand();
17986             this.restrictHeight();
17987             if(this.lastQuery == this.allQuery){
17988                 if(this.editable && !this.tickable){
17989                     this.inputEl().dom.select();
17990                 }
17991                 
17992                 if(
17993                     !this.selectByValue(this.value, true) &&
17994                     this.autoFocus && 
17995                     (
17996                         !this.store.lastOptions ||
17997                         typeof(this.store.lastOptions.add) == 'undefined' || 
17998                         this.store.lastOptions.add != true
17999                     )
18000                 ){
18001                     this.select(0, true);
18002                 }
18003             }else{
18004                 if(this.autoFocus){
18005                     this.selectNext();
18006                 }
18007                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18008                     this.taTask.delay(this.typeAheadDelay);
18009                 }
18010             }
18011         }else{
18012             this.onEmptyResults();
18013         }
18014         
18015         //this.el.focus();
18016     },
18017     // private
18018     onLoadException : function()
18019     {
18020         this.hasQuery = false;
18021         
18022         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18023             this.loading.hide();
18024         }
18025         
18026         if(this.tickable && this.editable){
18027             return;
18028         }
18029         
18030         this.collapse();
18031         // only causes errors at present
18032         //Roo.log(this.store.reader.jsonData);
18033         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18034             // fixme
18035             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18036         //}
18037         
18038         
18039     },
18040     // private
18041     onTypeAhead : function(){
18042         if(this.store.getCount() > 0){
18043             var r = this.store.getAt(0);
18044             var newValue = r.data[this.displayField];
18045             var len = newValue.length;
18046             var selStart = this.getRawValue().length;
18047             
18048             if(selStart != len){
18049                 this.setRawValue(newValue);
18050                 this.selectText(selStart, newValue.length);
18051             }
18052         }
18053     },
18054
18055     // private
18056     onSelect : function(record, index){
18057         
18058         if(this.fireEvent('beforeselect', this, record, index) !== false){
18059         
18060             this.setFromData(index > -1 ? record.data : false);
18061             
18062             this.collapse();
18063             this.fireEvent('select', this, record, index);
18064         }
18065     },
18066
18067     /**
18068      * Returns the currently selected field value or empty string if no value is set.
18069      * @return {String} value The selected value
18070      */
18071     getValue : function()
18072     {
18073         if(Roo.isIOS && this.useNativeIOS){
18074             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18075         }
18076         
18077         if(this.multiple){
18078             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18079         }
18080         
18081         if(this.valueField){
18082             return typeof this.value != 'undefined' ? this.value : '';
18083         }else{
18084             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18085         }
18086     },
18087     
18088     getRawValue : function()
18089     {
18090         if(Roo.isIOS && this.useNativeIOS){
18091             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18092         }
18093         
18094         var v = this.inputEl().getValue();
18095         
18096         return v;
18097     },
18098
18099     /**
18100      * Clears any text/value currently set in the field
18101      */
18102     clearValue : function(){
18103         
18104         if(this.hiddenField){
18105             this.hiddenField.dom.value = '';
18106         }
18107         this.value = '';
18108         this.setRawValue('');
18109         this.lastSelectionText = '';
18110         this.lastData = false;
18111         
18112         var close = this.closeTriggerEl();
18113         
18114         if(close){
18115             close.hide();
18116         }
18117         
18118         this.validate();
18119         
18120     },
18121
18122     /**
18123      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18124      * will be displayed in the field.  If the value does not match the data value of an existing item,
18125      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18126      * Otherwise the field will be blank (although the value will still be set).
18127      * @param {String} value The value to match
18128      */
18129     setValue : function(v)
18130     {
18131         if(Roo.isIOS && this.useNativeIOS){
18132             this.setIOSValue(v);
18133             return;
18134         }
18135         
18136         if(this.multiple){
18137             this.syncValue();
18138             return;
18139         }
18140         
18141         var text = v;
18142         if(this.valueField){
18143             var r = this.findRecord(this.valueField, v);
18144             if(r){
18145                 text = r.data[this.displayField];
18146             }else if(this.valueNotFoundText !== undefined){
18147                 text = this.valueNotFoundText;
18148             }
18149         }
18150         this.lastSelectionText = text;
18151         if(this.hiddenField){
18152             this.hiddenField.dom.value = v;
18153         }
18154         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18155         this.value = v;
18156         
18157         var close = this.closeTriggerEl();
18158         
18159         if(close){
18160             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18161         }
18162         
18163         this.validate();
18164     },
18165     /**
18166      * @property {Object} the last set data for the element
18167      */
18168     
18169     lastData : false,
18170     /**
18171      * Sets the value of the field based on a object which is related to the record format for the store.
18172      * @param {Object} value the value to set as. or false on reset?
18173      */
18174     setFromData : function(o){
18175         
18176         if(this.multiple){
18177             this.addItem(o);
18178             return;
18179         }
18180             
18181         var dv = ''; // display value
18182         var vv = ''; // value value..
18183         this.lastData = o;
18184         if (this.displayField) {
18185             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18186         } else {
18187             // this is an error condition!!!
18188             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18189         }
18190         
18191         if(this.valueField){
18192             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18193         }
18194         
18195         var close = this.closeTriggerEl();
18196         
18197         if(close){
18198             if(dv.length || vv * 1 > 0){
18199                 close.show() ;
18200                 this.blockFocus=true;
18201             } else {
18202                 close.hide();
18203             }             
18204         }
18205         
18206         if(this.hiddenField){
18207             this.hiddenField.dom.value = vv;
18208             
18209             this.lastSelectionText = dv;
18210             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18211             this.value = vv;
18212             return;
18213         }
18214         // no hidden field.. - we store the value in 'value', but still display
18215         // display field!!!!
18216         this.lastSelectionText = dv;
18217         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18218         this.value = vv;
18219         
18220         
18221         
18222     },
18223     // private
18224     reset : function(){
18225         // overridden so that last data is reset..
18226         
18227         if(this.multiple){
18228             this.clearItem();
18229             return;
18230         }
18231         
18232         this.setValue(this.originalValue);
18233         //this.clearInvalid();
18234         this.lastData = false;
18235         if (this.view) {
18236             this.view.clearSelections();
18237         }
18238         
18239         this.validate();
18240     },
18241     // private
18242     findRecord : function(prop, value){
18243         var record;
18244         if(this.store.getCount() > 0){
18245             this.store.each(function(r){
18246                 if(r.data[prop] == value){
18247                     record = r;
18248                     return false;
18249                 }
18250                 return true;
18251             });
18252         }
18253         return record;
18254     },
18255     
18256     getName: function()
18257     {
18258         // returns hidden if it's set..
18259         if (!this.rendered) {return ''};
18260         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18261         
18262     },
18263     // private
18264     onViewMove : function(e, t){
18265         this.inKeyMode = false;
18266     },
18267
18268     // private
18269     onViewOver : function(e, t){
18270         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18271             return;
18272         }
18273         var item = this.view.findItemFromChild(t);
18274         
18275         if(item){
18276             var index = this.view.indexOf(item);
18277             this.select(index, false);
18278         }
18279     },
18280
18281     // private
18282     onViewClick : function(view, doFocus, el, e)
18283     {
18284         var index = this.view.getSelectedIndexes()[0];
18285         
18286         var r = this.store.getAt(index);
18287         
18288         if(this.tickable){
18289             
18290             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18291                 return;
18292             }
18293             
18294             var rm = false;
18295             var _this = this;
18296             
18297             Roo.each(this.tickItems, function(v,k){
18298                 
18299                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18300                     Roo.log(v);
18301                     _this.tickItems.splice(k, 1);
18302                     
18303                     if(typeof(e) == 'undefined' && view == false){
18304                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18305                     }
18306                     
18307                     rm = true;
18308                     return;
18309                 }
18310             });
18311             
18312             if(rm){
18313                 return;
18314             }
18315             
18316             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18317                 this.tickItems.push(r.data);
18318             }
18319             
18320             if(typeof(e) == 'undefined' && view == false){
18321                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18322             }
18323                     
18324             return;
18325         }
18326         
18327         if(r){
18328             this.onSelect(r, index);
18329         }
18330         if(doFocus !== false && !this.blockFocus){
18331             this.inputEl().focus();
18332         }
18333     },
18334
18335     // private
18336     restrictHeight : function(){
18337         //this.innerList.dom.style.height = '';
18338         //var inner = this.innerList.dom;
18339         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18340         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18341         //this.list.beginUpdate();
18342         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18343         this.list.alignTo(this.inputEl(), this.listAlign);
18344         this.list.alignTo(this.inputEl(), this.listAlign);
18345         //this.list.endUpdate();
18346     },
18347
18348     // private
18349     onEmptyResults : function(){
18350         
18351         if(this.tickable && this.editable){
18352             this.hasFocus = false;
18353             this.restrictHeight();
18354             return;
18355         }
18356         
18357         this.collapse();
18358     },
18359
18360     /**
18361      * Returns true if the dropdown list is expanded, else false.
18362      */
18363     isExpanded : function(){
18364         return this.list.isVisible();
18365     },
18366
18367     /**
18368      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18369      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18370      * @param {String} value The data value of the item to select
18371      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18372      * selected item if it is not currently in view (defaults to true)
18373      * @return {Boolean} True if the value matched an item in the list, else false
18374      */
18375     selectByValue : function(v, scrollIntoView){
18376         if(v !== undefined && v !== null){
18377             var r = this.findRecord(this.valueField || this.displayField, v);
18378             if(r){
18379                 this.select(this.store.indexOf(r), scrollIntoView);
18380                 return true;
18381             }
18382         }
18383         return false;
18384     },
18385
18386     /**
18387      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18388      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18389      * @param {Number} index The zero-based index of the list item to select
18390      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18391      * selected item if it is not currently in view (defaults to true)
18392      */
18393     select : function(index, scrollIntoView){
18394         this.selectedIndex = index;
18395         this.view.select(index);
18396         if(scrollIntoView !== false){
18397             var el = this.view.getNode(index);
18398             /*
18399              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18400              */
18401             if(el){
18402                 this.list.scrollChildIntoView(el, false);
18403             }
18404         }
18405     },
18406
18407     // private
18408     selectNext : function(){
18409         var ct = this.store.getCount();
18410         if(ct > 0){
18411             if(this.selectedIndex == -1){
18412                 this.select(0);
18413             }else if(this.selectedIndex < ct-1){
18414                 this.select(this.selectedIndex+1);
18415             }
18416         }
18417     },
18418
18419     // private
18420     selectPrev : function(){
18421         var ct = this.store.getCount();
18422         if(ct > 0){
18423             if(this.selectedIndex == -1){
18424                 this.select(0);
18425             }else if(this.selectedIndex != 0){
18426                 this.select(this.selectedIndex-1);
18427             }
18428         }
18429     },
18430
18431     // private
18432     onKeyUp : function(e){
18433         if(this.editable !== false && !e.isSpecialKey()){
18434             this.lastKey = e.getKey();
18435             this.dqTask.delay(this.queryDelay);
18436         }
18437     },
18438
18439     // private
18440     validateBlur : function(){
18441         return !this.list || !this.list.isVisible();   
18442     },
18443
18444     // private
18445     initQuery : function(){
18446         
18447         var v = this.getRawValue();
18448         
18449         if(this.tickable && this.editable){
18450             v = this.tickableInputEl().getValue();
18451         }
18452         
18453         this.doQuery(v);
18454     },
18455
18456     // private
18457     doForce : function(){
18458         if(this.inputEl().dom.value.length > 0){
18459             this.inputEl().dom.value =
18460                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18461              
18462         }
18463     },
18464
18465     /**
18466      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18467      * query allowing the query action to be canceled if needed.
18468      * @param {String} query The SQL query to execute
18469      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18470      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18471      * saved in the current store (defaults to false)
18472      */
18473     doQuery : function(q, forceAll){
18474         
18475         if(q === undefined || q === null){
18476             q = '';
18477         }
18478         var qe = {
18479             query: q,
18480             forceAll: forceAll,
18481             combo: this,
18482             cancel:false
18483         };
18484         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18485             return false;
18486         }
18487         q = qe.query;
18488         
18489         forceAll = qe.forceAll;
18490         if(forceAll === true || (q.length >= this.minChars)){
18491             
18492             this.hasQuery = true;
18493             
18494             if(this.lastQuery != q || this.alwaysQuery){
18495                 this.lastQuery = q;
18496                 if(this.mode == 'local'){
18497                     this.selectedIndex = -1;
18498                     if(forceAll){
18499                         this.store.clearFilter();
18500                     }else{
18501                         
18502                         if(this.specialFilter){
18503                             this.fireEvent('specialfilter', this);
18504                             this.onLoad();
18505                             return;
18506                         }
18507                         
18508                         this.store.filter(this.displayField, q);
18509                     }
18510                     
18511                     this.store.fireEvent("datachanged", this.store);
18512                     
18513                     this.onLoad();
18514                     
18515                     
18516                 }else{
18517                     
18518                     this.store.baseParams[this.queryParam] = q;
18519                     
18520                     var options = {params : this.getParams(q)};
18521                     
18522                     if(this.loadNext){
18523                         options.add = true;
18524                         options.params.start = this.page * this.pageSize;
18525                     }
18526                     
18527                     this.store.load(options);
18528                     
18529                     /*
18530                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18531                      *  we should expand the list on onLoad
18532                      *  so command out it
18533                      */
18534 //                    this.expand();
18535                 }
18536             }else{
18537                 this.selectedIndex = -1;
18538                 this.onLoad();   
18539             }
18540         }
18541         
18542         this.loadNext = false;
18543     },
18544     
18545     // private
18546     getParams : function(q){
18547         var p = {};
18548         //p[this.queryParam] = q;
18549         
18550         if(this.pageSize){
18551             p.start = 0;
18552             p.limit = this.pageSize;
18553         }
18554         return p;
18555     },
18556
18557     /**
18558      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18559      */
18560     collapse : function(){
18561         if(!this.isExpanded()){
18562             return;
18563         }
18564         
18565         this.list.hide();
18566         
18567         this.hasFocus = false;
18568         
18569         if(this.tickable){
18570             this.okBtn.hide();
18571             this.cancelBtn.hide();
18572             this.trigger.show();
18573             
18574             if(this.editable){
18575                 this.tickableInputEl().dom.value = '';
18576                 this.tickableInputEl().blur();
18577             }
18578             
18579         }
18580         
18581         Roo.get(document).un('mousedown', this.collapseIf, this);
18582         Roo.get(document).un('mousewheel', this.collapseIf, this);
18583         if (!this.editable) {
18584             Roo.get(document).un('keydown', this.listKeyPress, this);
18585         }
18586         this.fireEvent('collapse', this);
18587         
18588         this.validate();
18589     },
18590
18591     // private
18592     collapseIf : function(e){
18593         var in_combo  = e.within(this.el);
18594         var in_list =  e.within(this.list);
18595         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18596         
18597         if (in_combo || in_list || is_list) {
18598             //e.stopPropagation();
18599             return;
18600         }
18601         
18602         if(this.tickable){
18603             this.onTickableFooterButtonClick(e, false, false);
18604         }
18605
18606         this.collapse();
18607         
18608     },
18609
18610     /**
18611      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18612      */
18613     expand : function(){
18614        
18615         if(this.isExpanded() || !this.hasFocus){
18616             return;
18617         }
18618         
18619         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18620         this.list.setWidth(lw);
18621         
18622         Roo.log('expand');
18623         
18624         this.list.show();
18625         
18626         this.restrictHeight();
18627         
18628         if(this.tickable){
18629             
18630             this.tickItems = Roo.apply([], this.item);
18631             
18632             this.okBtn.show();
18633             this.cancelBtn.show();
18634             this.trigger.hide();
18635             
18636             if(this.editable){
18637                 this.tickableInputEl().focus();
18638             }
18639             
18640         }
18641         
18642         Roo.get(document).on('mousedown', this.collapseIf, this);
18643         Roo.get(document).on('mousewheel', this.collapseIf, this);
18644         if (!this.editable) {
18645             Roo.get(document).on('keydown', this.listKeyPress, this);
18646         }
18647         
18648         this.fireEvent('expand', this);
18649     },
18650
18651     // private
18652     // Implements the default empty TriggerField.onTriggerClick function
18653     onTriggerClick : function(e)
18654     {
18655         Roo.log('trigger click');
18656         
18657         if(this.disabled || !this.triggerList){
18658             return;
18659         }
18660         
18661         this.page = 0;
18662         this.loadNext = false;
18663         
18664         if(this.isExpanded()){
18665             this.collapse();
18666             if (!this.blockFocus) {
18667                 this.inputEl().focus();
18668             }
18669             
18670         }else {
18671             this.hasFocus = true;
18672             if(this.triggerAction == 'all') {
18673                 this.doQuery(this.allQuery, true);
18674             } else {
18675                 this.doQuery(this.getRawValue());
18676             }
18677             if (!this.blockFocus) {
18678                 this.inputEl().focus();
18679             }
18680         }
18681     },
18682     
18683     onTickableTriggerClick : function(e)
18684     {
18685         if(this.disabled){
18686             return;
18687         }
18688         
18689         this.page = 0;
18690         this.loadNext = false;
18691         this.hasFocus = true;
18692         
18693         if(this.triggerAction == 'all') {
18694             this.doQuery(this.allQuery, true);
18695         } else {
18696             this.doQuery(this.getRawValue());
18697         }
18698     },
18699     
18700     onSearchFieldClick : function(e)
18701     {
18702         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18703             this.onTickableFooterButtonClick(e, false, false);
18704             return;
18705         }
18706         
18707         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18708             return;
18709         }
18710         
18711         this.page = 0;
18712         this.loadNext = false;
18713         this.hasFocus = true;
18714         
18715         if(this.triggerAction == 'all') {
18716             this.doQuery(this.allQuery, true);
18717         } else {
18718             this.doQuery(this.getRawValue());
18719         }
18720     },
18721     
18722     listKeyPress : function(e)
18723     {
18724         //Roo.log('listkeypress');
18725         // scroll to first matching element based on key pres..
18726         if (e.isSpecialKey()) {
18727             return false;
18728         }
18729         var k = String.fromCharCode(e.getKey()).toUpperCase();
18730         //Roo.log(k);
18731         var match  = false;
18732         var csel = this.view.getSelectedNodes();
18733         var cselitem = false;
18734         if (csel.length) {
18735             var ix = this.view.indexOf(csel[0]);
18736             cselitem  = this.store.getAt(ix);
18737             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18738                 cselitem = false;
18739             }
18740             
18741         }
18742         
18743         this.store.each(function(v) { 
18744             if (cselitem) {
18745                 // start at existing selection.
18746                 if (cselitem.id == v.id) {
18747                     cselitem = false;
18748                 }
18749                 return true;
18750             }
18751                 
18752             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18753                 match = this.store.indexOf(v);
18754                 return false;
18755             }
18756             return true;
18757         }, this);
18758         
18759         if (match === false) {
18760             return true; // no more action?
18761         }
18762         // scroll to?
18763         this.view.select(match);
18764         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18765         sn.scrollIntoView(sn.dom.parentNode, false);
18766     },
18767     
18768     onViewScroll : function(e, t){
18769         
18770         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){
18771             return;
18772         }
18773         
18774         this.hasQuery = true;
18775         
18776         this.loading = this.list.select('.loading', true).first();
18777         
18778         if(this.loading === null){
18779             this.list.createChild({
18780                 tag: 'div',
18781                 cls: 'loading roo-select2-more-results roo-select2-active',
18782                 html: 'Loading more results...'
18783             });
18784             
18785             this.loading = this.list.select('.loading', true).first();
18786             
18787             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18788             
18789             this.loading.hide();
18790         }
18791         
18792         this.loading.show();
18793         
18794         var _combo = this;
18795         
18796         this.page++;
18797         this.loadNext = true;
18798         
18799         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18800         
18801         return;
18802     },
18803     
18804     addItem : function(o)
18805     {   
18806         var dv = ''; // display value
18807         
18808         if (this.displayField) {
18809             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18810         } else {
18811             // this is an error condition!!!
18812             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18813         }
18814         
18815         if(!dv.length){
18816             return;
18817         }
18818         
18819         var choice = this.choices.createChild({
18820             tag: 'li',
18821             cls: 'roo-select2-search-choice',
18822             cn: [
18823                 {
18824                     tag: 'div',
18825                     html: dv
18826                 },
18827                 {
18828                     tag: 'a',
18829                     href: '#',
18830                     cls: 'roo-select2-search-choice-close fa fa-times',
18831                     tabindex: '-1'
18832                 }
18833             ]
18834             
18835         }, this.searchField);
18836         
18837         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18838         
18839         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18840         
18841         this.item.push(o);
18842         
18843         this.lastData = o;
18844         
18845         this.syncValue();
18846         
18847         this.inputEl().dom.value = '';
18848         
18849         this.validate();
18850     },
18851     
18852     onRemoveItem : function(e, _self, o)
18853     {
18854         e.preventDefault();
18855         
18856         this.lastItem = Roo.apply([], this.item);
18857         
18858         var index = this.item.indexOf(o.data) * 1;
18859         
18860         if( index < 0){
18861             Roo.log('not this item?!');
18862             return;
18863         }
18864         
18865         this.item.splice(index, 1);
18866         o.item.remove();
18867         
18868         this.syncValue();
18869         
18870         this.fireEvent('remove', this, e);
18871         
18872         this.validate();
18873         
18874     },
18875     
18876     syncValue : function()
18877     {
18878         if(!this.item.length){
18879             this.clearValue();
18880             return;
18881         }
18882             
18883         var value = [];
18884         var _this = this;
18885         Roo.each(this.item, function(i){
18886             if(_this.valueField){
18887                 value.push(i[_this.valueField]);
18888                 return;
18889             }
18890
18891             value.push(i);
18892         });
18893
18894         this.value = value.join(',');
18895
18896         if(this.hiddenField){
18897             this.hiddenField.dom.value = this.value;
18898         }
18899         
18900         this.store.fireEvent("datachanged", this.store);
18901         
18902         this.validate();
18903     },
18904     
18905     clearItem : function()
18906     {
18907         if(!this.multiple){
18908             return;
18909         }
18910         
18911         this.item = [];
18912         
18913         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18914            c.remove();
18915         });
18916         
18917         this.syncValue();
18918         
18919         this.validate();
18920         
18921         if(this.tickable && !Roo.isTouch){
18922             this.view.refresh();
18923         }
18924     },
18925     
18926     inputEl: function ()
18927     {
18928         if(Roo.isIOS && this.useNativeIOS){
18929             return this.el.select('select.roo-ios-select', true).first();
18930         }
18931         
18932         if(Roo.isTouch && this.mobileTouchView){
18933             return this.el.select('input.form-control',true).first();
18934         }
18935         
18936         if(this.tickable){
18937             return this.searchField;
18938         }
18939         
18940         return this.el.select('input.form-control',true).first();
18941     },
18942     
18943     onTickableFooterButtonClick : function(e, btn, el)
18944     {
18945         e.preventDefault();
18946         
18947         this.lastItem = Roo.apply([], this.item);
18948         
18949         if(btn && btn.name == 'cancel'){
18950             this.tickItems = Roo.apply([], this.item);
18951             this.collapse();
18952             return;
18953         }
18954         
18955         this.clearItem();
18956         
18957         var _this = this;
18958         
18959         Roo.each(this.tickItems, function(o){
18960             _this.addItem(o);
18961         });
18962         
18963         this.collapse();
18964         
18965     },
18966     
18967     validate : function()
18968     {
18969         if(this.getVisibilityEl().hasClass('hidden')){
18970             return true;
18971         }
18972         
18973         var v = this.getRawValue();
18974         
18975         if(this.multiple){
18976             v = this.getValue();
18977         }
18978         
18979         if(this.disabled || this.allowBlank || v.length){
18980             this.markValid();
18981             return true;
18982         }
18983         
18984         this.markInvalid();
18985         return false;
18986     },
18987     
18988     tickableInputEl : function()
18989     {
18990         if(!this.tickable || !this.editable){
18991             return this.inputEl();
18992         }
18993         
18994         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18995     },
18996     
18997     
18998     getAutoCreateTouchView : function()
18999     {
19000         var id = Roo.id();
19001         
19002         var cfg = {
19003             cls: 'form-group' //input-group
19004         };
19005         
19006         var input =  {
19007             tag: 'input',
19008             id : id,
19009             type : this.inputType,
19010             cls : 'form-control x-combo-noedit',
19011             autocomplete: 'new-password',
19012             placeholder : this.placeholder || '',
19013             readonly : true
19014         };
19015         
19016         if (this.name) {
19017             input.name = this.name;
19018         }
19019         
19020         if (this.size) {
19021             input.cls += ' input-' + this.size;
19022         }
19023         
19024         if (this.disabled) {
19025             input.disabled = true;
19026         }
19027         
19028         var inputblock = {
19029             cls : 'roo-combobox-wrap',
19030             cn : [
19031                 input
19032             ]
19033         };
19034         
19035         if(this.before){
19036             inputblock.cls += ' input-group';
19037             
19038             inputblock.cn.unshift({
19039                 tag :'span',
19040                 cls : 'input-group-addon input-group-prepend input-group-text',
19041                 html : this.before
19042             });
19043         }
19044         
19045         if(this.removable && !this.multiple){
19046             inputblock.cls += ' roo-removable';
19047             
19048             inputblock.cn.push({
19049                 tag: 'button',
19050                 html : 'x',
19051                 cls : 'roo-combo-removable-btn close'
19052             });
19053         }
19054
19055         if(this.hasFeedback && !this.allowBlank){
19056             
19057             inputblock.cls += ' has-feedback';
19058             
19059             inputblock.cn.push({
19060                 tag: 'span',
19061                 cls: 'glyphicon form-control-feedback'
19062             });
19063             
19064         }
19065         
19066         if (this.after) {
19067             
19068             inputblock.cls += (this.before) ? '' : ' input-group';
19069             
19070             inputblock.cn.push({
19071                 tag :'span',
19072                 cls : 'input-group-addon input-group-append input-group-text',
19073                 html : this.after
19074             });
19075         }
19076
19077         
19078         var ibwrap = inputblock;
19079         
19080         if(this.multiple){
19081             ibwrap = {
19082                 tag: 'ul',
19083                 cls: 'roo-select2-choices',
19084                 cn:[
19085                     {
19086                         tag: 'li',
19087                         cls: 'roo-select2-search-field',
19088                         cn: [
19089
19090                             inputblock
19091                         ]
19092                     }
19093                 ]
19094             };
19095         
19096             
19097         }
19098         
19099         var combobox = {
19100             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19101             cn: [
19102                 {
19103                     tag: 'input',
19104                     type : 'hidden',
19105                     cls: 'form-hidden-field'
19106                 },
19107                 ibwrap
19108             ]
19109         };
19110         
19111         if(!this.multiple && this.showToggleBtn){
19112             
19113             var caret = {
19114                 cls: 'caret'
19115             };
19116             
19117             if (this.caret != false) {
19118                 caret = {
19119                      tag: 'i',
19120                      cls: 'fa fa-' + this.caret
19121                 };
19122                 
19123             }
19124             
19125             combobox.cn.push({
19126                 tag :'span',
19127                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19128                 cn : [
19129                     Roo.bootstrap.version == 3 ? caret : '',
19130                     {
19131                         tag: 'span',
19132                         cls: 'combobox-clear',
19133                         cn  : [
19134                             {
19135                                 tag : 'i',
19136                                 cls: 'icon-remove'
19137                             }
19138                         ]
19139                     }
19140                 ]
19141
19142             })
19143         }
19144         
19145         if(this.multiple){
19146             combobox.cls += ' roo-select2-container-multi';
19147         }
19148         
19149         var required =  this.allowBlank ?  {
19150                     tag : 'i',
19151                     style: 'display: none'
19152                 } : {
19153                    tag : 'i',
19154                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19155                    tooltip : 'This field is required'
19156                 };
19157         
19158         var align = this.labelAlign || this.parentLabelAlign();
19159         
19160         if (align ==='left' && this.fieldLabel.length) {
19161
19162             cfg.cn = [
19163                 required,
19164                 {
19165                     tag: 'label',
19166                     cls : 'control-label col-form-label',
19167                     html : this.fieldLabel
19168
19169                 },
19170                 {
19171                     cls : 'roo-combobox-wrap ', 
19172                     cn: [
19173                         combobox
19174                     ]
19175                 }
19176             ];
19177             
19178             var labelCfg = cfg.cn[1];
19179             var contentCfg = cfg.cn[2];
19180             
19181
19182             if(this.indicatorpos == 'right'){
19183                 cfg.cn = [
19184                     {
19185                         tag: 'label',
19186                         'for' :  id,
19187                         cls : 'control-label col-form-label',
19188                         cn : [
19189                             {
19190                                 tag : 'span',
19191                                 html : this.fieldLabel
19192                             },
19193                             required
19194                         ]
19195                     },
19196                     {
19197                         cls : "roo-combobox-wrap ",
19198                         cn: [
19199                             combobox
19200                         ]
19201                     }
19202
19203                 ];
19204                 
19205                 labelCfg = cfg.cn[0];
19206                 contentCfg = cfg.cn[1];
19207             }
19208             
19209            
19210             
19211             if(this.labelWidth > 12){
19212                 labelCfg.style = "width: " + this.labelWidth + 'px';
19213             }
19214            
19215             if(this.labelWidth < 13 && this.labelmd == 0){
19216                 this.labelmd = this.labelWidth;
19217             }
19218             
19219             if(this.labellg > 0){
19220                 labelCfg.cls += ' col-lg-' + this.labellg;
19221                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19222             }
19223             
19224             if(this.labelmd > 0){
19225                 labelCfg.cls += ' col-md-' + this.labelmd;
19226                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19227             }
19228             
19229             if(this.labelsm > 0){
19230                 labelCfg.cls += ' col-sm-' + this.labelsm;
19231                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19232             }
19233             
19234             if(this.labelxs > 0){
19235                 labelCfg.cls += ' col-xs-' + this.labelxs;
19236                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19237             }
19238                 
19239                 
19240         } else if ( this.fieldLabel.length) {
19241             cfg.cn = [
19242                required,
19243                 {
19244                     tag: 'label',
19245                     cls : 'control-label',
19246                     html : this.fieldLabel
19247
19248                 },
19249                 {
19250                     cls : '', 
19251                     cn: [
19252                         combobox
19253                     ]
19254                 }
19255             ];
19256             
19257             if(this.indicatorpos == 'right'){
19258                 cfg.cn = [
19259                     {
19260                         tag: 'label',
19261                         cls : 'control-label',
19262                         html : this.fieldLabel,
19263                         cn : [
19264                             required
19265                         ]
19266                     },
19267                     {
19268                         cls : '', 
19269                         cn: [
19270                             combobox
19271                         ]
19272                     }
19273                 ];
19274             }
19275         } else {
19276             cfg.cn = combobox;    
19277         }
19278         
19279         
19280         var settings = this;
19281         
19282         ['xs','sm','md','lg'].map(function(size){
19283             if (settings[size]) {
19284                 cfg.cls += ' col-' + size + '-' + settings[size];
19285             }
19286         });
19287         
19288         return cfg;
19289     },
19290     
19291     initTouchView : function()
19292     {
19293         this.renderTouchView();
19294         
19295         this.touchViewEl.on('scroll', function(){
19296             this.el.dom.scrollTop = 0;
19297         }, this);
19298         
19299         this.originalValue = this.getValue();
19300         
19301         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19302         
19303         this.inputEl().on("click", this.showTouchView, this);
19304         if (this.triggerEl) {
19305             this.triggerEl.on("click", this.showTouchView, this);
19306         }
19307         
19308         
19309         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19310         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19311         
19312         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19313         
19314         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19315         this.store.on('load', this.onTouchViewLoad, this);
19316         this.store.on('loadexception', this.onTouchViewLoadException, this);
19317         
19318         if(this.hiddenName){
19319             
19320             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19321             
19322             this.hiddenField.dom.value =
19323                 this.hiddenValue !== undefined ? this.hiddenValue :
19324                 this.value !== undefined ? this.value : '';
19325         
19326             this.el.dom.removeAttribute('name');
19327             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19328         }
19329         
19330         if(this.multiple){
19331             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19332             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19333         }
19334         
19335         if(this.removable && !this.multiple){
19336             var close = this.closeTriggerEl();
19337             if(close){
19338                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19339                 close.on('click', this.removeBtnClick, this, close);
19340             }
19341         }
19342         /*
19343          * fix the bug in Safari iOS8
19344          */
19345         this.inputEl().on("focus", function(e){
19346             document.activeElement.blur();
19347         }, this);
19348         
19349         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19350         
19351         return;
19352         
19353         
19354     },
19355     
19356     renderTouchView : function()
19357     {
19358         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19359         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19360         
19361         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19362         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19363         
19364         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19365         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19366         this.touchViewBodyEl.setStyle('overflow', 'auto');
19367         
19368         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19369         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19370         
19371         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19372         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19373         
19374     },
19375     
19376     showTouchView : function()
19377     {
19378         if(this.disabled){
19379             return;
19380         }
19381         
19382         this.touchViewHeaderEl.hide();
19383
19384         if(this.modalTitle.length){
19385             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19386             this.touchViewHeaderEl.show();
19387         }
19388
19389         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19390         this.touchViewEl.show();
19391
19392         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19393         
19394         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19395         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19396
19397         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19398
19399         if(this.modalTitle.length){
19400             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19401         }
19402         
19403         this.touchViewBodyEl.setHeight(bodyHeight);
19404
19405         if(this.animate){
19406             var _this = this;
19407             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19408         }else{
19409             this.touchViewEl.addClass(['in','show']);
19410         }
19411         
19412         if(this._touchViewMask){
19413             Roo.get(document.body).addClass("x-body-masked");
19414             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19415             this._touchViewMask.setStyle('z-index', 10000);
19416             this._touchViewMask.addClass('show');
19417         }
19418         
19419         this.doTouchViewQuery();
19420         
19421     },
19422     
19423     hideTouchView : function()
19424     {
19425         this.touchViewEl.removeClass(['in','show']);
19426
19427         if(this.animate){
19428             var _this = this;
19429             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19430         }else{
19431             this.touchViewEl.setStyle('display', 'none');
19432         }
19433         
19434         if(this._touchViewMask){
19435             this._touchViewMask.removeClass('show');
19436             Roo.get(document.body).removeClass("x-body-masked");
19437         }
19438     },
19439     
19440     setTouchViewValue : function()
19441     {
19442         if(this.multiple){
19443             this.clearItem();
19444         
19445             var _this = this;
19446
19447             Roo.each(this.tickItems, function(o){
19448                 this.addItem(o);
19449             }, this);
19450         }
19451         
19452         this.hideTouchView();
19453     },
19454     
19455     doTouchViewQuery : function()
19456     {
19457         var qe = {
19458             query: '',
19459             forceAll: true,
19460             combo: this,
19461             cancel:false
19462         };
19463         
19464         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19465             return false;
19466         }
19467         
19468         if(!this.alwaysQuery || this.mode == 'local'){
19469             this.onTouchViewLoad();
19470             return;
19471         }
19472         
19473         this.store.load();
19474     },
19475     
19476     onTouchViewBeforeLoad : function(combo,opts)
19477     {
19478         return;
19479     },
19480
19481     // private
19482     onTouchViewLoad : function()
19483     {
19484         if(this.store.getCount() < 1){
19485             this.onTouchViewEmptyResults();
19486             return;
19487         }
19488         
19489         this.clearTouchView();
19490         
19491         var rawValue = this.getRawValue();
19492         
19493         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19494         
19495         this.tickItems = [];
19496         
19497         this.store.data.each(function(d, rowIndex){
19498             var row = this.touchViewListGroup.createChild(template);
19499             
19500             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19501                 row.addClass(d.data.cls);
19502             }
19503             
19504             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19505                 var cfg = {
19506                     data : d.data,
19507                     html : d.data[this.displayField]
19508                 };
19509                 
19510                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19511                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19512                 }
19513             }
19514             row.removeClass('selected');
19515             if(!this.multiple && this.valueField &&
19516                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19517             {
19518                 // radio buttons..
19519                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19520                 row.addClass('selected');
19521             }
19522             
19523             if(this.multiple && this.valueField &&
19524                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19525             {
19526                 
19527                 // checkboxes...
19528                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19529                 this.tickItems.push(d.data);
19530             }
19531             
19532             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19533             
19534         }, this);
19535         
19536         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19537         
19538         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19539
19540         if(this.modalTitle.length){
19541             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19542         }
19543
19544         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19545         
19546         if(this.mobile_restrict_height && listHeight < bodyHeight){
19547             this.touchViewBodyEl.setHeight(listHeight);
19548         }
19549         
19550         var _this = this;
19551         
19552         if(firstChecked && listHeight > bodyHeight){
19553             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19554         }
19555         
19556     },
19557     
19558     onTouchViewLoadException : function()
19559     {
19560         this.hideTouchView();
19561     },
19562     
19563     onTouchViewEmptyResults : function()
19564     {
19565         this.clearTouchView();
19566         
19567         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19568         
19569         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19570         
19571     },
19572     
19573     clearTouchView : function()
19574     {
19575         this.touchViewListGroup.dom.innerHTML = '';
19576     },
19577     
19578     onTouchViewClick : function(e, el, o)
19579     {
19580         e.preventDefault();
19581         
19582         var row = o.row;
19583         var rowIndex = o.rowIndex;
19584         
19585         var r = this.store.getAt(rowIndex);
19586         
19587         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19588             
19589             if(!this.multiple){
19590                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19591                     c.dom.removeAttribute('checked');
19592                 }, this);
19593
19594                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19595
19596                 this.setFromData(r.data);
19597
19598                 var close = this.closeTriggerEl();
19599
19600                 if(close){
19601                     close.show();
19602                 }
19603
19604                 this.hideTouchView();
19605
19606                 this.fireEvent('select', this, r, rowIndex);
19607
19608                 return;
19609             }
19610
19611             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19612                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19613                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19614                 return;
19615             }
19616
19617             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19618             this.addItem(r.data);
19619             this.tickItems.push(r.data);
19620         }
19621     },
19622     
19623     getAutoCreateNativeIOS : function()
19624     {
19625         var cfg = {
19626             cls: 'form-group' //input-group,
19627         };
19628         
19629         var combobox =  {
19630             tag: 'select',
19631             cls : 'roo-ios-select'
19632         };
19633         
19634         if (this.name) {
19635             combobox.name = this.name;
19636         }
19637         
19638         if (this.disabled) {
19639             combobox.disabled = true;
19640         }
19641         
19642         var settings = this;
19643         
19644         ['xs','sm','md','lg'].map(function(size){
19645             if (settings[size]) {
19646                 cfg.cls += ' col-' + size + '-' + settings[size];
19647             }
19648         });
19649         
19650         cfg.cn = combobox;
19651         
19652         return cfg;
19653         
19654     },
19655     
19656     initIOSView : function()
19657     {
19658         this.store.on('load', this.onIOSViewLoad, this);
19659         
19660         return;
19661     },
19662     
19663     onIOSViewLoad : function()
19664     {
19665         if(this.store.getCount() < 1){
19666             return;
19667         }
19668         
19669         this.clearIOSView();
19670         
19671         if(this.allowBlank) {
19672             
19673             var default_text = '-- SELECT --';
19674             
19675             if(this.placeholder.length){
19676                 default_text = this.placeholder;
19677             }
19678             
19679             if(this.emptyTitle.length){
19680                 default_text += ' - ' + this.emptyTitle + ' -';
19681             }
19682             
19683             var opt = this.inputEl().createChild({
19684                 tag: 'option',
19685                 value : 0,
19686                 html : default_text
19687             });
19688             
19689             var o = {};
19690             o[this.valueField] = 0;
19691             o[this.displayField] = default_text;
19692             
19693             this.ios_options.push({
19694                 data : o,
19695                 el : opt
19696             });
19697             
19698         }
19699         
19700         this.store.data.each(function(d, rowIndex){
19701             
19702             var html = '';
19703             
19704             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19705                 html = d.data[this.displayField];
19706             }
19707             
19708             var value = '';
19709             
19710             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19711                 value = d.data[this.valueField];
19712             }
19713             
19714             var option = {
19715                 tag: 'option',
19716                 value : value,
19717                 html : html
19718             };
19719             
19720             if(this.value == d.data[this.valueField]){
19721                 option['selected'] = true;
19722             }
19723             
19724             var opt = this.inputEl().createChild(option);
19725             
19726             this.ios_options.push({
19727                 data : d.data,
19728                 el : opt
19729             });
19730             
19731         }, this);
19732         
19733         this.inputEl().on('change', function(){
19734            this.fireEvent('select', this);
19735         }, this);
19736         
19737     },
19738     
19739     clearIOSView: function()
19740     {
19741         this.inputEl().dom.innerHTML = '';
19742         
19743         this.ios_options = [];
19744     },
19745     
19746     setIOSValue: function(v)
19747     {
19748         this.value = v;
19749         
19750         if(!this.ios_options){
19751             return;
19752         }
19753         
19754         Roo.each(this.ios_options, function(opts){
19755            
19756            opts.el.dom.removeAttribute('selected');
19757            
19758            if(opts.data[this.valueField] != v){
19759                return;
19760            }
19761            
19762            opts.el.dom.setAttribute('selected', true);
19763            
19764         }, this);
19765     }
19766
19767     /** 
19768     * @cfg {Boolean} grow 
19769     * @hide 
19770     */
19771     /** 
19772     * @cfg {Number} growMin 
19773     * @hide 
19774     */
19775     /** 
19776     * @cfg {Number} growMax 
19777     * @hide 
19778     */
19779     /**
19780      * @hide
19781      * @method autoSize
19782      */
19783 });
19784
19785 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19786     
19787     header : {
19788         tag: 'div',
19789         cls: 'modal-header',
19790         cn: [
19791             {
19792                 tag: 'h4',
19793                 cls: 'modal-title'
19794             }
19795         ]
19796     },
19797     
19798     body : {
19799         tag: 'div',
19800         cls: 'modal-body',
19801         cn: [
19802             {
19803                 tag: 'ul',
19804                 cls: 'list-group'
19805             }
19806         ]
19807     },
19808     
19809     listItemRadio : {
19810         tag: 'li',
19811         cls: 'list-group-item',
19812         cn: [
19813             {
19814                 tag: 'span',
19815                 cls: 'roo-combobox-list-group-item-value'
19816             },
19817             {
19818                 tag: 'div',
19819                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19820                 cn: [
19821                     {
19822                         tag: 'input',
19823                         type: 'radio'
19824                     },
19825                     {
19826                         tag: 'label'
19827                     }
19828                 ]
19829             }
19830         ]
19831     },
19832     
19833     listItemCheckbox : {
19834         tag: 'li',
19835         cls: 'list-group-item',
19836         cn: [
19837             {
19838                 tag: 'span',
19839                 cls: 'roo-combobox-list-group-item-value'
19840             },
19841             {
19842                 tag: 'div',
19843                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19844                 cn: [
19845                     {
19846                         tag: 'input',
19847                         type: 'checkbox'
19848                     },
19849                     {
19850                         tag: 'label'
19851                     }
19852                 ]
19853             }
19854         ]
19855     },
19856     
19857     emptyResult : {
19858         tag: 'div',
19859         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19860     },
19861     
19862     footer : {
19863         tag: 'div',
19864         cls: 'modal-footer',
19865         cn: [
19866             {
19867                 tag: 'div',
19868                 cls: 'row',
19869                 cn: [
19870                     {
19871                         tag: 'div',
19872                         cls: 'col-xs-6 text-left',
19873                         cn: {
19874                             tag: 'button',
19875                             cls: 'btn btn-danger roo-touch-view-cancel',
19876                             html: 'Cancel'
19877                         }
19878                     },
19879                     {
19880                         tag: 'div',
19881                         cls: 'col-xs-6 text-right',
19882                         cn: {
19883                             tag: 'button',
19884                             cls: 'btn btn-success roo-touch-view-ok',
19885                             html: 'OK'
19886                         }
19887                     }
19888                 ]
19889             }
19890         ]
19891         
19892     }
19893 });
19894
19895 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19896     
19897     touchViewTemplate : {
19898         tag: 'div',
19899         cls: 'modal fade roo-combobox-touch-view',
19900         cn: [
19901             {
19902                 tag: 'div',
19903                 cls: 'modal-dialog',
19904                 style : 'position:fixed', // we have to fix position....
19905                 cn: [
19906                     {
19907                         tag: 'div',
19908                         cls: 'modal-content',
19909                         cn: [
19910                             Roo.bootstrap.form.ComboBox.header,
19911                             Roo.bootstrap.form.ComboBox.body,
19912                             Roo.bootstrap.form.ComboBox.footer
19913                         ]
19914                     }
19915                 ]
19916             }
19917         ]
19918     }
19919 });/*
19920  * Based on:
19921  * Ext JS Library 1.1.1
19922  * Copyright(c) 2006-2007, Ext JS, LLC.
19923  *
19924  * Originally Released Under LGPL - original licence link has changed is not relivant.
19925  *
19926  * Fork - LGPL
19927  * <script type="text/javascript">
19928  */
19929
19930 /**
19931  * @class Roo.View
19932  * @extends Roo.util.Observable
19933  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19934  * This class also supports single and multi selection modes. <br>
19935  * Create a data model bound view:
19936  <pre><code>
19937  var store = new Roo.data.Store(...);
19938
19939  var view = new Roo.View({
19940     el : "my-element",
19941     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19942  
19943     singleSelect: true,
19944     selectedClass: "ydataview-selected",
19945     store: store
19946  });
19947
19948  // listen for node click?
19949  view.on("click", function(vw, index, node, e){
19950  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19951  });
19952
19953  // load XML data
19954  dataModel.load("foobar.xml");
19955  </code></pre>
19956  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19957  * <br><br>
19958  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19959  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19960  * 
19961  * Note: old style constructor is still suported (container, template, config)
19962  * 
19963  * @constructor
19964  * Create a new View
19965  * @param {Object} config The config object
19966  * 
19967  */
19968 Roo.View = function(config, depreciated_tpl, depreciated_config){
19969     
19970     this.parent = false;
19971     
19972     if (typeof(depreciated_tpl) == 'undefined') {
19973         // new way.. - universal constructor.
19974         Roo.apply(this, config);
19975         this.el  = Roo.get(this.el);
19976     } else {
19977         // old format..
19978         this.el  = Roo.get(config);
19979         this.tpl = depreciated_tpl;
19980         Roo.apply(this, depreciated_config);
19981     }
19982     this.wrapEl  = this.el.wrap().wrap();
19983     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19984     
19985     
19986     if(typeof(this.tpl) == "string"){
19987         this.tpl = new Roo.Template(this.tpl);
19988     } else {
19989         // support xtype ctors..
19990         this.tpl = new Roo.factory(this.tpl, Roo);
19991     }
19992     
19993     
19994     this.tpl.compile();
19995     
19996     /** @private */
19997     this.addEvents({
19998         /**
19999          * @event beforeclick
20000          * Fires before a click is processed. Returns false to cancel the default action.
20001          * @param {Roo.View} this
20002          * @param {Number} index The index of the target node
20003          * @param {HTMLElement} node The target node
20004          * @param {Roo.EventObject} e The raw event object
20005          */
20006             "beforeclick" : true,
20007         /**
20008          * @event click
20009          * Fires when a template node is clicked.
20010          * @param {Roo.View} this
20011          * @param {Number} index The index of the target node
20012          * @param {HTMLElement} node The target node
20013          * @param {Roo.EventObject} e The raw event object
20014          */
20015             "click" : true,
20016         /**
20017          * @event dblclick
20018          * Fires when a template node is double clicked.
20019          * @param {Roo.View} this
20020          * @param {Number} index The index of the target node
20021          * @param {HTMLElement} node The target node
20022          * @param {Roo.EventObject} e The raw event object
20023          */
20024             "dblclick" : true,
20025         /**
20026          * @event contextmenu
20027          * Fires when a template node is right clicked.
20028          * @param {Roo.View} this
20029          * @param {Number} index The index of the target node
20030          * @param {HTMLElement} node The target node
20031          * @param {Roo.EventObject} e The raw event object
20032          */
20033             "contextmenu" : true,
20034         /**
20035          * @event selectionchange
20036          * Fires when the selected nodes change.
20037          * @param {Roo.View} this
20038          * @param {Array} selections Array of the selected nodes
20039          */
20040             "selectionchange" : true,
20041     
20042         /**
20043          * @event beforeselect
20044          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20045          * @param {Roo.View} this
20046          * @param {HTMLElement} node The node to be selected
20047          * @param {Array} selections Array of currently selected nodes
20048          */
20049             "beforeselect" : true,
20050         /**
20051          * @event preparedata
20052          * Fires on every row to render, to allow you to change the data.
20053          * @param {Roo.View} this
20054          * @param {Object} data to be rendered (change this)
20055          */
20056           "preparedata" : true
20057           
20058           
20059         });
20060
20061
20062
20063     this.el.on({
20064         "click": this.onClick,
20065         "dblclick": this.onDblClick,
20066         "contextmenu": this.onContextMenu,
20067         scope:this
20068     });
20069
20070     this.selections = [];
20071     this.nodes = [];
20072     this.cmp = new Roo.CompositeElementLite([]);
20073     if(this.store){
20074         this.store = Roo.factory(this.store, Roo.data);
20075         this.setStore(this.store, true);
20076     }
20077     
20078     if ( this.footer && this.footer.xtype) {
20079            
20080          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20081         
20082         this.footer.dataSource = this.store;
20083         this.footer.container = fctr;
20084         this.footer = Roo.factory(this.footer, Roo);
20085         fctr.insertFirst(this.el);
20086         
20087         // this is a bit insane - as the paging toolbar seems to detach the el..
20088 //        dom.parentNode.parentNode.parentNode
20089          // they get detached?
20090     }
20091     
20092     
20093     Roo.View.superclass.constructor.call(this);
20094     
20095     
20096 };
20097
20098 Roo.extend(Roo.View, Roo.util.Observable, {
20099     
20100      /**
20101      * @cfg {Roo.data.Store} store Data store to load data from.
20102      */
20103     store : false,
20104     
20105     /**
20106      * @cfg {String|Roo.Element} el The container element.
20107      */
20108     el : '',
20109     
20110     /**
20111      * @cfg {String|Roo.Template} tpl The template used by this View 
20112      */
20113     tpl : false,
20114     /**
20115      * @cfg {String} dataName the named area of the template to use as the data area
20116      *                          Works with domtemplates roo-name="name"
20117      */
20118     dataName: false,
20119     /**
20120      * @cfg {String} selectedClass The css class to add to selected nodes
20121      */
20122     selectedClass : "x-view-selected",
20123      /**
20124      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20125      */
20126     emptyText : "",
20127     
20128     /**
20129      * @cfg {String} text to display on mask (default Loading)
20130      */
20131     mask : false,
20132     /**
20133      * @cfg {Boolean} multiSelect Allow multiple selection
20134      */
20135     multiSelect : false,
20136     /**
20137      * @cfg {Boolean} singleSelect Allow single selection
20138      */
20139     singleSelect:  false,
20140     
20141     /**
20142      * @cfg {Boolean} toggleSelect - selecting 
20143      */
20144     toggleSelect : false,
20145     
20146     /**
20147      * @cfg {Boolean} tickable - selecting 
20148      */
20149     tickable : false,
20150     
20151     /**
20152      * Returns the element this view is bound to.
20153      * @return {Roo.Element}
20154      */
20155     getEl : function(){
20156         return this.wrapEl;
20157     },
20158     
20159     
20160
20161     /**
20162      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20163      */
20164     refresh : function(){
20165         //Roo.log('refresh');
20166         var t = this.tpl;
20167         
20168         // if we are using something like 'domtemplate', then
20169         // the what gets used is:
20170         // t.applySubtemplate(NAME, data, wrapping data..)
20171         // the outer template then get' applied with
20172         //     the store 'extra data'
20173         // and the body get's added to the
20174         //      roo-name="data" node?
20175         //      <span class='roo-tpl-{name}'></span> ?????
20176         
20177         
20178         
20179         this.clearSelections();
20180         this.el.update("");
20181         var html = [];
20182         var records = this.store.getRange();
20183         if(records.length < 1) {
20184             
20185             // is this valid??  = should it render a template??
20186             
20187             this.el.update(this.emptyText);
20188             return;
20189         }
20190         var el = this.el;
20191         if (this.dataName) {
20192             this.el.update(t.apply(this.store.meta)); //????
20193             el = this.el.child('.roo-tpl-' + this.dataName);
20194         }
20195         
20196         for(var i = 0, len = records.length; i < len; i++){
20197             var data = this.prepareData(records[i].data, i, records[i]);
20198             this.fireEvent("preparedata", this, data, i, records[i]);
20199             
20200             var d = Roo.apply({}, data);
20201             
20202             if(this.tickable){
20203                 Roo.apply(d, {'roo-id' : Roo.id()});
20204                 
20205                 var _this = this;
20206             
20207                 Roo.each(this.parent.item, function(item){
20208                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20209                         return;
20210                     }
20211                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20212                 });
20213             }
20214             
20215             html[html.length] = Roo.util.Format.trim(
20216                 this.dataName ?
20217                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20218                     t.apply(d)
20219             );
20220         }
20221         
20222         
20223         
20224         el.update(html.join(""));
20225         this.nodes = el.dom.childNodes;
20226         this.updateIndexes(0);
20227     },
20228     
20229
20230     /**
20231      * Function to override to reformat the data that is sent to
20232      * the template for each node.
20233      * DEPRICATED - use the preparedata event handler.
20234      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20235      * a JSON object for an UpdateManager bound view).
20236      */
20237     prepareData : function(data, index, record)
20238     {
20239         this.fireEvent("preparedata", this, data, index, record);
20240         return data;
20241     },
20242
20243     onUpdate : function(ds, record){
20244         // Roo.log('on update');   
20245         this.clearSelections();
20246         var index = this.store.indexOf(record);
20247         var n = this.nodes[index];
20248         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20249         n.parentNode.removeChild(n);
20250         this.updateIndexes(index, index);
20251     },
20252
20253     
20254     
20255 // --------- FIXME     
20256     onAdd : function(ds, records, index)
20257     {
20258         //Roo.log(['on Add', ds, records, index] );        
20259         this.clearSelections();
20260         if(this.nodes.length == 0){
20261             this.refresh();
20262             return;
20263         }
20264         var n = this.nodes[index];
20265         for(var i = 0, len = records.length; i < len; i++){
20266             var d = this.prepareData(records[i].data, i, records[i]);
20267             if(n){
20268                 this.tpl.insertBefore(n, d);
20269             }else{
20270                 
20271                 this.tpl.append(this.el, d);
20272             }
20273         }
20274         this.updateIndexes(index);
20275     },
20276
20277     onRemove : function(ds, record, index){
20278        // Roo.log('onRemove');
20279         this.clearSelections();
20280         var el = this.dataName  ?
20281             this.el.child('.roo-tpl-' + this.dataName) :
20282             this.el; 
20283         
20284         el.dom.removeChild(this.nodes[index]);
20285         this.updateIndexes(index);
20286     },
20287
20288     /**
20289      * Refresh an individual node.
20290      * @param {Number} index
20291      */
20292     refreshNode : function(index){
20293         this.onUpdate(this.store, this.store.getAt(index));
20294     },
20295
20296     updateIndexes : function(startIndex, endIndex){
20297         var ns = this.nodes;
20298         startIndex = startIndex || 0;
20299         endIndex = endIndex || ns.length - 1;
20300         for(var i = startIndex; i <= endIndex; i++){
20301             ns[i].nodeIndex = i;
20302         }
20303     },
20304
20305     /**
20306      * Changes the data store this view uses and refresh the view.
20307      * @param {Store} store
20308      */
20309     setStore : function(store, initial){
20310         if(!initial && this.store){
20311             this.store.un("datachanged", this.refresh);
20312             this.store.un("add", this.onAdd);
20313             this.store.un("remove", this.onRemove);
20314             this.store.un("update", this.onUpdate);
20315             this.store.un("clear", this.refresh);
20316             this.store.un("beforeload", this.onBeforeLoad);
20317             this.store.un("load", this.onLoad);
20318             this.store.un("loadexception", this.onLoad);
20319         }
20320         if(store){
20321           
20322             store.on("datachanged", this.refresh, this);
20323             store.on("add", this.onAdd, this);
20324             store.on("remove", this.onRemove, this);
20325             store.on("update", this.onUpdate, this);
20326             store.on("clear", this.refresh, this);
20327             store.on("beforeload", this.onBeforeLoad, this);
20328             store.on("load", this.onLoad, this);
20329             store.on("loadexception", this.onLoad, this);
20330         }
20331         
20332         if(store){
20333             this.refresh();
20334         }
20335     },
20336     /**
20337      * onbeforeLoad - masks the loading area.
20338      *
20339      */
20340     onBeforeLoad : function(store,opts)
20341     {
20342          //Roo.log('onBeforeLoad');   
20343         if (!opts.add) {
20344             this.el.update("");
20345         }
20346         this.el.mask(this.mask ? this.mask : "Loading" ); 
20347     },
20348     onLoad : function ()
20349     {
20350         this.el.unmask();
20351     },
20352     
20353
20354     /**
20355      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20356      * @param {HTMLElement} node
20357      * @return {HTMLElement} The template node
20358      */
20359     findItemFromChild : function(node){
20360         var el = this.dataName  ?
20361             this.el.child('.roo-tpl-' + this.dataName,true) :
20362             this.el.dom; 
20363         
20364         if(!node || node.parentNode == el){
20365                     return node;
20366             }
20367             var p = node.parentNode;
20368             while(p && p != el){
20369             if(p.parentNode == el){
20370                 return p;
20371             }
20372             p = p.parentNode;
20373         }
20374             return null;
20375     },
20376
20377     /** @ignore */
20378     onClick : function(e){
20379         var item = this.findItemFromChild(e.getTarget());
20380         if(item){
20381             var index = this.indexOf(item);
20382             if(this.onItemClick(item, index, e) !== false){
20383                 this.fireEvent("click", this, index, item, e);
20384             }
20385         }else{
20386             this.clearSelections();
20387         }
20388     },
20389
20390     /** @ignore */
20391     onContextMenu : function(e){
20392         var item = this.findItemFromChild(e.getTarget());
20393         if(item){
20394             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20395         }
20396     },
20397
20398     /** @ignore */
20399     onDblClick : function(e){
20400         var item = this.findItemFromChild(e.getTarget());
20401         if(item){
20402             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20403         }
20404     },
20405
20406     onItemClick : function(item, index, e)
20407     {
20408         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20409             return false;
20410         }
20411         if (this.toggleSelect) {
20412             var m = this.isSelected(item) ? 'unselect' : 'select';
20413             //Roo.log(m);
20414             var _t = this;
20415             _t[m](item, true, false);
20416             return true;
20417         }
20418         if(this.multiSelect || this.singleSelect){
20419             if(this.multiSelect && e.shiftKey && this.lastSelection){
20420                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20421             }else{
20422                 this.select(item, this.multiSelect && e.ctrlKey);
20423                 this.lastSelection = item;
20424             }
20425             
20426             if(!this.tickable){
20427                 e.preventDefault();
20428             }
20429             
20430         }
20431         return true;
20432     },
20433
20434     /**
20435      * Get the number of selected nodes.
20436      * @return {Number}
20437      */
20438     getSelectionCount : function(){
20439         return this.selections.length;
20440     },
20441
20442     /**
20443      * Get the currently selected nodes.
20444      * @return {Array} An array of HTMLElements
20445      */
20446     getSelectedNodes : function(){
20447         return this.selections;
20448     },
20449
20450     /**
20451      * Get the indexes of the selected nodes.
20452      * @return {Array}
20453      */
20454     getSelectedIndexes : function(){
20455         var indexes = [], s = this.selections;
20456         for(var i = 0, len = s.length; i < len; i++){
20457             indexes.push(s[i].nodeIndex);
20458         }
20459         return indexes;
20460     },
20461
20462     /**
20463      * Clear all selections
20464      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20465      */
20466     clearSelections : function(suppressEvent){
20467         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20468             this.cmp.elements = this.selections;
20469             this.cmp.removeClass(this.selectedClass);
20470             this.selections = [];
20471             if(!suppressEvent){
20472                 this.fireEvent("selectionchange", this, this.selections);
20473             }
20474         }
20475     },
20476
20477     /**
20478      * Returns true if the passed node is selected
20479      * @param {HTMLElement/Number} node The node or node index
20480      * @return {Boolean}
20481      */
20482     isSelected : function(node){
20483         var s = this.selections;
20484         if(s.length < 1){
20485             return false;
20486         }
20487         node = this.getNode(node);
20488         return s.indexOf(node) !== -1;
20489     },
20490
20491     /**
20492      * Selects nodes.
20493      * @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
20494      * @param {Boolean} keepExisting (optional) true to keep existing selections
20495      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20496      */
20497     select : function(nodeInfo, keepExisting, suppressEvent){
20498         if(nodeInfo instanceof Array){
20499             if(!keepExisting){
20500                 this.clearSelections(true);
20501             }
20502             for(var i = 0, len = nodeInfo.length; i < len; i++){
20503                 this.select(nodeInfo[i], true, true);
20504             }
20505             return;
20506         } 
20507         var node = this.getNode(nodeInfo);
20508         if(!node || this.isSelected(node)){
20509             return; // already selected.
20510         }
20511         if(!keepExisting){
20512             this.clearSelections(true);
20513         }
20514         
20515         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20516             Roo.fly(node).addClass(this.selectedClass);
20517             this.selections.push(node);
20518             if(!suppressEvent){
20519                 this.fireEvent("selectionchange", this, this.selections);
20520             }
20521         }
20522         
20523         
20524     },
20525       /**
20526      * Unselects nodes.
20527      * @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
20528      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20529      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20530      */
20531     unselect : function(nodeInfo, keepExisting, suppressEvent)
20532     {
20533         if(nodeInfo instanceof Array){
20534             Roo.each(this.selections, function(s) {
20535                 this.unselect(s, nodeInfo);
20536             }, this);
20537             return;
20538         }
20539         var node = this.getNode(nodeInfo);
20540         if(!node || !this.isSelected(node)){
20541             //Roo.log("not selected");
20542             return; // not selected.
20543         }
20544         // fireevent???
20545         var ns = [];
20546         Roo.each(this.selections, function(s) {
20547             if (s == node ) {
20548                 Roo.fly(node).removeClass(this.selectedClass);
20549
20550                 return;
20551             }
20552             ns.push(s);
20553         },this);
20554         
20555         this.selections= ns;
20556         this.fireEvent("selectionchange", this, this.selections);
20557     },
20558
20559     /**
20560      * Gets a template node.
20561      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20562      * @return {HTMLElement} The node or null if it wasn't found
20563      */
20564     getNode : function(nodeInfo){
20565         if(typeof nodeInfo == "string"){
20566             return document.getElementById(nodeInfo);
20567         }else if(typeof nodeInfo == "number"){
20568             return this.nodes[nodeInfo];
20569         }
20570         return nodeInfo;
20571     },
20572
20573     /**
20574      * Gets a range template nodes.
20575      * @param {Number} startIndex
20576      * @param {Number} endIndex
20577      * @return {Array} An array of nodes
20578      */
20579     getNodes : function(start, end){
20580         var ns = this.nodes;
20581         start = start || 0;
20582         end = typeof end == "undefined" ? ns.length - 1 : end;
20583         var nodes = [];
20584         if(start <= end){
20585             for(var i = start; i <= end; i++){
20586                 nodes.push(ns[i]);
20587             }
20588         } else{
20589             for(var i = start; i >= end; i--){
20590                 nodes.push(ns[i]);
20591             }
20592         }
20593         return nodes;
20594     },
20595
20596     /**
20597      * Finds the index of the passed node
20598      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20599      * @return {Number} The index of the node or -1
20600      */
20601     indexOf : function(node){
20602         node = this.getNode(node);
20603         if(typeof node.nodeIndex == "number"){
20604             return node.nodeIndex;
20605         }
20606         var ns = this.nodes;
20607         for(var i = 0, len = ns.length; i < len; i++){
20608             if(ns[i] == node){
20609                 return i;
20610             }
20611         }
20612         return -1;
20613     }
20614 });
20615 /*
20616  * - LGPL
20617  *
20618  * based on jquery fullcalendar
20619  * 
20620  */
20621
20622 Roo.bootstrap = Roo.bootstrap || {};
20623 /**
20624  * @class Roo.bootstrap.Calendar
20625  * @extends Roo.bootstrap.Component
20626  * Bootstrap Calendar class
20627  * @cfg {Boolean} loadMask (true|false) default false
20628  * @cfg {Object} header generate the user specific header of the calendar, default false
20629
20630  * @constructor
20631  * Create a new Container
20632  * @param {Object} config The config object
20633  */
20634
20635
20636
20637 Roo.bootstrap.Calendar = function(config){
20638     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20639      this.addEvents({
20640         /**
20641              * @event select
20642              * Fires when a date is selected
20643              * @param {DatePicker} this
20644              * @param {Date} date The selected date
20645              */
20646         'select': true,
20647         /**
20648              * @event monthchange
20649              * Fires when the displayed month changes 
20650              * @param {DatePicker} this
20651              * @param {Date} date The selected month
20652              */
20653         'monthchange': true,
20654         /**
20655              * @event evententer
20656              * Fires when mouse over an event
20657              * @param {Calendar} this
20658              * @param {event} Event
20659              */
20660         'evententer': true,
20661         /**
20662              * @event eventleave
20663              * Fires when the mouse leaves an
20664              * @param {Calendar} this
20665              * @param {event}
20666              */
20667         'eventleave': true,
20668         /**
20669              * @event eventclick
20670              * Fires when the mouse click an
20671              * @param {Calendar} this
20672              * @param {event}
20673              */
20674         'eventclick': true
20675         
20676     });
20677
20678 };
20679
20680 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20681     
20682           /**
20683      * @cfg {Roo.data.Store} store
20684      * The data source for the calendar
20685      */
20686         store : false,
20687      /**
20688      * @cfg {Number} startDay
20689      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20690      */
20691     startDay : 0,
20692     
20693     loadMask : false,
20694     
20695     header : false,
20696       
20697     getAutoCreate : function(){
20698         
20699         
20700         var fc_button = function(name, corner, style, content ) {
20701             return Roo.apply({},{
20702                 tag : 'span',
20703                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20704                          (corner.length ?
20705                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20706                             ''
20707                         ),
20708                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20709                 unselectable: 'on'
20710             });
20711         };
20712         
20713         var header = {};
20714         
20715         if(!this.header){
20716             header = {
20717                 tag : 'table',
20718                 cls : 'fc-header',
20719                 style : 'width:100%',
20720                 cn : [
20721                     {
20722                         tag: 'tr',
20723                         cn : [
20724                             {
20725                                 tag : 'td',
20726                                 cls : 'fc-header-left',
20727                                 cn : [
20728                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20729                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20730                                     { tag: 'span', cls: 'fc-header-space' },
20731                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20732
20733
20734                                 ]
20735                             },
20736
20737                             {
20738                                 tag : 'td',
20739                                 cls : 'fc-header-center',
20740                                 cn : [
20741                                     {
20742                                         tag: 'span',
20743                                         cls: 'fc-header-title',
20744                                         cn : {
20745                                             tag: 'H2',
20746                                             html : 'month / year'
20747                                         }
20748                                     }
20749
20750                                 ]
20751                             },
20752                             {
20753                                 tag : 'td',
20754                                 cls : 'fc-header-right',
20755                                 cn : [
20756                               /*      fc_button('month', 'left', '', 'month' ),
20757                                     fc_button('week', '', '', 'week' ),
20758                                     fc_button('day', 'right', '', 'day' )
20759                                 */    
20760
20761                                 ]
20762                             }
20763
20764                         ]
20765                     }
20766                 ]
20767             };
20768         }
20769         
20770         header = this.header;
20771         
20772        
20773         var cal_heads = function() {
20774             var ret = [];
20775             // fixme - handle this.
20776             
20777             for (var i =0; i < Date.dayNames.length; i++) {
20778                 var d = Date.dayNames[i];
20779                 ret.push({
20780                     tag: 'th',
20781                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20782                     html : d.substring(0,3)
20783                 });
20784                 
20785             }
20786             ret[0].cls += ' fc-first';
20787             ret[6].cls += ' fc-last';
20788             return ret;
20789         };
20790         var cal_cell = function(n) {
20791             return  {
20792                 tag: 'td',
20793                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20794                 cn : [
20795                     {
20796                         cn : [
20797                             {
20798                                 cls: 'fc-day-number',
20799                                 html: 'D'
20800                             },
20801                             {
20802                                 cls: 'fc-day-content',
20803                              
20804                                 cn : [
20805                                      {
20806                                         style: 'position: relative;' // height: 17px;
20807                                     }
20808                                 ]
20809                             }
20810                             
20811                             
20812                         ]
20813                     }
20814                 ]
20815                 
20816             }
20817         };
20818         var cal_rows = function() {
20819             
20820             var ret = [];
20821             for (var r = 0; r < 6; r++) {
20822                 var row= {
20823                     tag : 'tr',
20824                     cls : 'fc-week',
20825                     cn : []
20826                 };
20827                 
20828                 for (var i =0; i < Date.dayNames.length; i++) {
20829                     var d = Date.dayNames[i];
20830                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20831
20832                 }
20833                 row.cn[0].cls+=' fc-first';
20834                 row.cn[0].cn[0].style = 'min-height:90px';
20835                 row.cn[6].cls+=' fc-last';
20836                 ret.push(row);
20837                 
20838             }
20839             ret[0].cls += ' fc-first';
20840             ret[4].cls += ' fc-prev-last';
20841             ret[5].cls += ' fc-last';
20842             return ret;
20843             
20844         };
20845         
20846         var cal_table = {
20847             tag: 'table',
20848             cls: 'fc-border-separate',
20849             style : 'width:100%',
20850             cellspacing  : 0,
20851             cn : [
20852                 { 
20853                     tag: 'thead',
20854                     cn : [
20855                         { 
20856                             tag: 'tr',
20857                             cls : 'fc-first fc-last',
20858                             cn : cal_heads()
20859                         }
20860                     ]
20861                 },
20862                 { 
20863                     tag: 'tbody',
20864                     cn : cal_rows()
20865                 }
20866                   
20867             ]
20868         };
20869          
20870          var cfg = {
20871             cls : 'fc fc-ltr',
20872             cn : [
20873                 header,
20874                 {
20875                     cls : 'fc-content',
20876                     style : "position: relative;",
20877                     cn : [
20878                         {
20879                             cls : 'fc-view fc-view-month fc-grid',
20880                             style : 'position: relative',
20881                             unselectable : 'on',
20882                             cn : [
20883                                 {
20884                                     cls : 'fc-event-container',
20885                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20886                                 },
20887                                 cal_table
20888                             ]
20889                         }
20890                     ]
20891     
20892                 }
20893            ] 
20894             
20895         };
20896         
20897          
20898         
20899         return cfg;
20900     },
20901     
20902     
20903     initEvents : function()
20904     {
20905         if(!this.store){
20906             throw "can not find store for calendar";
20907         }
20908         
20909         var mark = {
20910             tag: "div",
20911             cls:"x-dlg-mask",
20912             style: "text-align:center",
20913             cn: [
20914                 {
20915                     tag: "div",
20916                     style: "background-color:white;width:50%;margin:250 auto",
20917                     cn: [
20918                         {
20919                             tag: "img",
20920                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20921                         },
20922                         {
20923                             tag: "span",
20924                             html: "Loading"
20925                         }
20926                         
20927                     ]
20928                 }
20929             ]
20930         };
20931         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20932         
20933         var size = this.el.select('.fc-content', true).first().getSize();
20934         this.maskEl.setSize(size.width, size.height);
20935         this.maskEl.enableDisplayMode("block");
20936         if(!this.loadMask){
20937             this.maskEl.hide();
20938         }
20939         
20940         this.store = Roo.factory(this.store, Roo.data);
20941         this.store.on('load', this.onLoad, this);
20942         this.store.on('beforeload', this.onBeforeLoad, this);
20943         
20944         this.resize();
20945         
20946         this.cells = this.el.select('.fc-day',true);
20947         //Roo.log(this.cells);
20948         this.textNodes = this.el.query('.fc-day-number');
20949         this.cells.addClassOnOver('fc-state-hover');
20950         
20951         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20952         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20953         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20954         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20955         
20956         this.on('monthchange', this.onMonthChange, this);
20957         
20958         this.update(new Date().clearTime());
20959     },
20960     
20961     resize : function() {
20962         var sz  = this.el.getSize();
20963         
20964         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20965         this.el.select('.fc-day-content div',true).setHeight(34);
20966     },
20967     
20968     
20969     // private
20970     showPrevMonth : function(e){
20971         this.update(this.activeDate.add("mo", -1));
20972     },
20973     showToday : function(e){
20974         this.update(new Date().clearTime());
20975     },
20976     // private
20977     showNextMonth : function(e){
20978         this.update(this.activeDate.add("mo", 1));
20979     },
20980
20981     // private
20982     showPrevYear : function(){
20983         this.update(this.activeDate.add("y", -1));
20984     },
20985
20986     // private
20987     showNextYear : function(){
20988         this.update(this.activeDate.add("y", 1));
20989     },
20990
20991     
20992    // private
20993     update : function(date)
20994     {
20995         var vd = this.activeDate;
20996         this.activeDate = date;
20997 //        if(vd && this.el){
20998 //            var t = date.getTime();
20999 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21000 //                Roo.log('using add remove');
21001 //                
21002 //                this.fireEvent('monthchange', this, date);
21003 //                
21004 //                this.cells.removeClass("fc-state-highlight");
21005 //                this.cells.each(function(c){
21006 //                   if(c.dateValue == t){
21007 //                       c.addClass("fc-state-highlight");
21008 //                       setTimeout(function(){
21009 //                            try{c.dom.firstChild.focus();}catch(e){}
21010 //                       }, 50);
21011 //                       return false;
21012 //                   }
21013 //                   return true;
21014 //                });
21015 //                return;
21016 //            }
21017 //        }
21018         
21019         var days = date.getDaysInMonth();
21020         
21021         var firstOfMonth = date.getFirstDateOfMonth();
21022         var startingPos = firstOfMonth.getDay()-this.startDay;
21023         
21024         if(startingPos < this.startDay){
21025             startingPos += 7;
21026         }
21027         
21028         var pm = date.add(Date.MONTH, -1);
21029         var prevStart = pm.getDaysInMonth()-startingPos;
21030 //        
21031         this.cells = this.el.select('.fc-day',true);
21032         this.textNodes = this.el.query('.fc-day-number');
21033         this.cells.addClassOnOver('fc-state-hover');
21034         
21035         var cells = this.cells.elements;
21036         var textEls = this.textNodes;
21037         
21038         Roo.each(cells, function(cell){
21039             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21040         });
21041         
21042         days += startingPos;
21043
21044         // convert everything to numbers so it's fast
21045         var day = 86400000;
21046         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21047         //Roo.log(d);
21048         //Roo.log(pm);
21049         //Roo.log(prevStart);
21050         
21051         var today = new Date().clearTime().getTime();
21052         var sel = date.clearTime().getTime();
21053         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21054         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21055         var ddMatch = this.disabledDatesRE;
21056         var ddText = this.disabledDatesText;
21057         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21058         var ddaysText = this.disabledDaysText;
21059         var format = this.format;
21060         
21061         var setCellClass = function(cal, cell){
21062             cell.row = 0;
21063             cell.events = [];
21064             cell.more = [];
21065             //Roo.log('set Cell Class');
21066             cell.title = "";
21067             var t = d.getTime();
21068             
21069             //Roo.log(d);
21070             
21071             cell.dateValue = t;
21072             if(t == today){
21073                 cell.className += " fc-today";
21074                 cell.className += " fc-state-highlight";
21075                 cell.title = cal.todayText;
21076             }
21077             if(t == sel){
21078                 // disable highlight in other month..
21079                 //cell.className += " fc-state-highlight";
21080                 
21081             }
21082             // disabling
21083             if(t < min) {
21084                 cell.className = " fc-state-disabled";
21085                 cell.title = cal.minText;
21086                 return;
21087             }
21088             if(t > max) {
21089                 cell.className = " fc-state-disabled";
21090                 cell.title = cal.maxText;
21091                 return;
21092             }
21093             if(ddays){
21094                 if(ddays.indexOf(d.getDay()) != -1){
21095                     cell.title = ddaysText;
21096                     cell.className = " fc-state-disabled";
21097                 }
21098             }
21099             if(ddMatch && format){
21100                 var fvalue = d.dateFormat(format);
21101                 if(ddMatch.test(fvalue)){
21102                     cell.title = ddText.replace("%0", fvalue);
21103                     cell.className = " fc-state-disabled";
21104                 }
21105             }
21106             
21107             if (!cell.initialClassName) {
21108                 cell.initialClassName = cell.dom.className;
21109             }
21110             
21111             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21112         };
21113
21114         var i = 0;
21115         
21116         for(; i < startingPos; i++) {
21117             textEls[i].innerHTML = (++prevStart);
21118             d.setDate(d.getDate()+1);
21119             
21120             cells[i].className = "fc-past fc-other-month";
21121             setCellClass(this, cells[i]);
21122         }
21123         
21124         var intDay = 0;
21125         
21126         for(; i < days; i++){
21127             intDay = i - startingPos + 1;
21128             textEls[i].innerHTML = (intDay);
21129             d.setDate(d.getDate()+1);
21130             
21131             cells[i].className = ''; // "x-date-active";
21132             setCellClass(this, cells[i]);
21133         }
21134         var extraDays = 0;
21135         
21136         for(; i < 42; i++) {
21137             textEls[i].innerHTML = (++extraDays);
21138             d.setDate(d.getDate()+1);
21139             
21140             cells[i].className = "fc-future fc-other-month";
21141             setCellClass(this, cells[i]);
21142         }
21143         
21144         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21145         
21146         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21147         
21148         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21149         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21150         
21151         if(totalRows != 6){
21152             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21153             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21154         }
21155         
21156         this.fireEvent('monthchange', this, date);
21157         
21158         
21159         /*
21160         if(!this.internalRender){
21161             var main = this.el.dom.firstChild;
21162             var w = main.offsetWidth;
21163             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21164             Roo.fly(main).setWidth(w);
21165             this.internalRender = true;
21166             // opera does not respect the auto grow header center column
21167             // then, after it gets a width opera refuses to recalculate
21168             // without a second pass
21169             if(Roo.isOpera && !this.secondPass){
21170                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21171                 this.secondPass = true;
21172                 this.update.defer(10, this, [date]);
21173             }
21174         }
21175         */
21176         
21177     },
21178     
21179     findCell : function(dt) {
21180         dt = dt.clearTime().getTime();
21181         var ret = false;
21182         this.cells.each(function(c){
21183             //Roo.log("check " +c.dateValue + '?=' + dt);
21184             if(c.dateValue == dt){
21185                 ret = c;
21186                 return false;
21187             }
21188             return true;
21189         });
21190         
21191         return ret;
21192     },
21193     
21194     findCells : function(ev) {
21195         var s = ev.start.clone().clearTime().getTime();
21196        // Roo.log(s);
21197         var e= ev.end.clone().clearTime().getTime();
21198        // Roo.log(e);
21199         var ret = [];
21200         this.cells.each(function(c){
21201              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21202             
21203             if(c.dateValue > e){
21204                 return ;
21205             }
21206             if(c.dateValue < s){
21207                 return ;
21208             }
21209             ret.push(c);
21210         });
21211         
21212         return ret;    
21213     },
21214     
21215 //    findBestRow: function(cells)
21216 //    {
21217 //        var ret = 0;
21218 //        
21219 //        for (var i =0 ; i < cells.length;i++) {
21220 //            ret  = Math.max(cells[i].rows || 0,ret);
21221 //        }
21222 //        return ret;
21223 //        
21224 //    },
21225     
21226     
21227     addItem : function(ev)
21228     {
21229         // look for vertical location slot in
21230         var cells = this.findCells(ev);
21231         
21232 //        ev.row = this.findBestRow(cells);
21233         
21234         // work out the location.
21235         
21236         var crow = false;
21237         var rows = [];
21238         for(var i =0; i < cells.length; i++) {
21239             
21240             cells[i].row = cells[0].row;
21241             
21242             if(i == 0){
21243                 cells[i].row = cells[i].row + 1;
21244             }
21245             
21246             if (!crow) {
21247                 crow = {
21248                     start : cells[i],
21249                     end :  cells[i]
21250                 };
21251                 continue;
21252             }
21253             if (crow.start.getY() == cells[i].getY()) {
21254                 // on same row.
21255                 crow.end = cells[i];
21256                 continue;
21257             }
21258             // different row.
21259             rows.push(crow);
21260             crow = {
21261                 start: cells[i],
21262                 end : cells[i]
21263             };
21264             
21265         }
21266         
21267         rows.push(crow);
21268         ev.els = [];
21269         ev.rows = rows;
21270         ev.cells = cells;
21271         
21272         cells[0].events.push(ev);
21273         
21274         this.calevents.push(ev);
21275     },
21276     
21277     clearEvents: function() {
21278         
21279         if(!this.calevents){
21280             return;
21281         }
21282         
21283         Roo.each(this.cells.elements, function(c){
21284             c.row = 0;
21285             c.events = [];
21286             c.more = [];
21287         });
21288         
21289         Roo.each(this.calevents, function(e) {
21290             Roo.each(e.els, function(el) {
21291                 el.un('mouseenter' ,this.onEventEnter, this);
21292                 el.un('mouseleave' ,this.onEventLeave, this);
21293                 el.remove();
21294             },this);
21295         },this);
21296         
21297         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21298             e.remove();
21299         });
21300         
21301     },
21302     
21303     renderEvents: function()
21304     {   
21305         var _this = this;
21306         
21307         this.cells.each(function(c) {
21308             
21309             if(c.row < 5){
21310                 return;
21311             }
21312             
21313             var ev = c.events;
21314             
21315             var r = 4;
21316             if(c.row != c.events.length){
21317                 r = 4 - (4 - (c.row - c.events.length));
21318             }
21319             
21320             c.events = ev.slice(0, r);
21321             c.more = ev.slice(r);
21322             
21323             if(c.more.length && c.more.length == 1){
21324                 c.events.push(c.more.pop());
21325             }
21326             
21327             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21328             
21329         });
21330             
21331         this.cells.each(function(c) {
21332             
21333             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21334             
21335             
21336             for (var e = 0; e < c.events.length; e++){
21337                 var ev = c.events[e];
21338                 var rows = ev.rows;
21339                 
21340                 for(var i = 0; i < rows.length; i++) {
21341                 
21342                     // how many rows should it span..
21343
21344                     var  cfg = {
21345                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21346                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21347
21348                         unselectable : "on",
21349                         cn : [
21350                             {
21351                                 cls: 'fc-event-inner',
21352                                 cn : [
21353     //                                {
21354     //                                  tag:'span',
21355     //                                  cls: 'fc-event-time',
21356     //                                  html : cells.length > 1 ? '' : ev.time
21357     //                                },
21358                                     {
21359                                       tag:'span',
21360                                       cls: 'fc-event-title',
21361                                       html : String.format('{0}', ev.title)
21362                                     }
21363
21364
21365                                 ]
21366                             },
21367                             {
21368                                 cls: 'ui-resizable-handle ui-resizable-e',
21369                                 html : '&nbsp;&nbsp;&nbsp'
21370                             }
21371
21372                         ]
21373                     };
21374
21375                     if (i == 0) {
21376                         cfg.cls += ' fc-event-start';
21377                     }
21378                     if ((i+1) == rows.length) {
21379                         cfg.cls += ' fc-event-end';
21380                     }
21381
21382                     var ctr = _this.el.select('.fc-event-container',true).first();
21383                     var cg = ctr.createChild(cfg);
21384
21385                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21386                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21387
21388                     var r = (c.more.length) ? 1 : 0;
21389                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21390                     cg.setWidth(ebox.right - sbox.x -2);
21391
21392                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21393                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21394                     cg.on('click', _this.onEventClick, _this, ev);
21395
21396                     ev.els.push(cg);
21397                     
21398                 }
21399                 
21400             }
21401             
21402             
21403             if(c.more.length){
21404                 var  cfg = {
21405                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21406                     style : 'position: absolute',
21407                     unselectable : "on",
21408                     cn : [
21409                         {
21410                             cls: 'fc-event-inner',
21411                             cn : [
21412                                 {
21413                                   tag:'span',
21414                                   cls: 'fc-event-title',
21415                                   html : 'More'
21416                                 }
21417
21418
21419                             ]
21420                         },
21421                         {
21422                             cls: 'ui-resizable-handle ui-resizable-e',
21423                             html : '&nbsp;&nbsp;&nbsp'
21424                         }
21425
21426                     ]
21427                 };
21428
21429                 var ctr = _this.el.select('.fc-event-container',true).first();
21430                 var cg = ctr.createChild(cfg);
21431
21432                 var sbox = c.select('.fc-day-content',true).first().getBox();
21433                 var ebox = c.select('.fc-day-content',true).first().getBox();
21434                 //Roo.log(cg);
21435                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21436                 cg.setWidth(ebox.right - sbox.x -2);
21437
21438                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21439                 
21440             }
21441             
21442         });
21443         
21444         
21445         
21446     },
21447     
21448     onEventEnter: function (e, el,event,d) {
21449         this.fireEvent('evententer', this, el, event);
21450     },
21451     
21452     onEventLeave: function (e, el,event,d) {
21453         this.fireEvent('eventleave', this, el, event);
21454     },
21455     
21456     onEventClick: function (e, el,event,d) {
21457         this.fireEvent('eventclick', this, el, event);
21458     },
21459     
21460     onMonthChange: function () {
21461         this.store.load();
21462     },
21463     
21464     onMoreEventClick: function(e, el, more)
21465     {
21466         var _this = this;
21467         
21468         this.calpopover.placement = 'right';
21469         this.calpopover.setTitle('More');
21470         
21471         this.calpopover.setContent('');
21472         
21473         var ctr = this.calpopover.el.select('.popover-content', true).first();
21474         
21475         Roo.each(more, function(m){
21476             var cfg = {
21477                 cls : 'fc-event-hori fc-event-draggable',
21478                 html : m.title
21479             };
21480             var cg = ctr.createChild(cfg);
21481             
21482             cg.on('click', _this.onEventClick, _this, m);
21483         });
21484         
21485         this.calpopover.show(el);
21486         
21487         
21488     },
21489     
21490     onLoad: function () 
21491     {   
21492         this.calevents = [];
21493         var cal = this;
21494         
21495         if(this.store.getCount() > 0){
21496             this.store.data.each(function(d){
21497                cal.addItem({
21498                     id : d.data.id,
21499                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21500                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21501                     time : d.data.start_time,
21502                     title : d.data.title,
21503                     description : d.data.description,
21504                     venue : d.data.venue
21505                 });
21506             });
21507         }
21508         
21509         this.renderEvents();
21510         
21511         if(this.calevents.length && this.loadMask){
21512             this.maskEl.hide();
21513         }
21514     },
21515     
21516     onBeforeLoad: function()
21517     {
21518         this.clearEvents();
21519         if(this.loadMask){
21520             this.maskEl.show();
21521         }
21522     }
21523 });
21524
21525  
21526  /*
21527  * - LGPL
21528  *
21529  * element
21530  * 
21531  */
21532
21533 /**
21534  * @class Roo.bootstrap.Popover
21535  * @extends Roo.bootstrap.Component
21536  * @parent none builder
21537  * @children Roo.bootstrap.Component
21538  * Bootstrap Popover class
21539  * @cfg {String} html contents of the popover   (or false to use children..)
21540  * @cfg {String} title of popover (or false to hide)
21541  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21542  * @cfg {String} trigger click || hover (or false to trigger manually)
21543  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21544  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21545  *      - if false and it has a 'parent' then it will be automatically added to that element
21546  *      - if string - Roo.get  will be called 
21547  * @cfg {Number} delay - delay before showing
21548  
21549  * @constructor
21550  * Create a new Popover
21551  * @param {Object} config The config object
21552  */
21553
21554 Roo.bootstrap.Popover = function(config){
21555     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21556     
21557     this.addEvents({
21558         // raw events
21559          /**
21560          * @event show
21561          * After the popover show
21562          * 
21563          * @param {Roo.bootstrap.Popover} this
21564          */
21565         "show" : true,
21566         /**
21567          * @event hide
21568          * After the popover hide
21569          * 
21570          * @param {Roo.bootstrap.Popover} this
21571          */
21572         "hide" : true
21573     });
21574 };
21575
21576 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21577     
21578     title: false,
21579     html: false,
21580     
21581     placement : 'right',
21582     trigger : 'hover', // hover
21583     modal : false,
21584     delay : 0,
21585     
21586     over: false,
21587     
21588     can_build_overlaid : false,
21589     
21590     maskEl : false, // the mask element
21591     headerEl : false,
21592     contentEl : false,
21593     alignEl : false, // when show is called with an element - this get's stored.
21594     
21595     getChildContainer : function()
21596     {
21597         return this.contentEl;
21598         
21599     },
21600     getPopoverHeader : function()
21601     {
21602         this.title = true; // flag not to hide it..
21603         this.headerEl.addClass('p-0');
21604         return this.headerEl
21605     },
21606     
21607     
21608     getAutoCreate : function(){
21609          
21610         var cfg = {
21611            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21612            style: 'display:block',
21613            cn : [
21614                 {
21615                     cls : 'arrow'
21616                 },
21617                 {
21618                     cls : 'popover-inner ',
21619                     cn : [
21620                         {
21621                             tag: 'h3',
21622                             cls: 'popover-title popover-header',
21623                             html : this.title === false ? '' : this.title
21624                         },
21625                         {
21626                             cls : 'popover-content popover-body '  + (this.cls || ''),
21627                             html : this.html || ''
21628                         }
21629                     ]
21630                     
21631                 }
21632            ]
21633         };
21634         
21635         return cfg;
21636     },
21637     /**
21638      * @param {string} the title
21639      */
21640     setTitle: function(str)
21641     {
21642         this.title = str;
21643         if (this.el) {
21644             this.headerEl.dom.innerHTML = str;
21645         }
21646         
21647     },
21648     /**
21649      * @param {string} the body content
21650      */
21651     setContent: function(str)
21652     {
21653         this.html = str;
21654         if (this.contentEl) {
21655             this.contentEl.dom.innerHTML = str;
21656         }
21657         
21658     },
21659     // as it get's added to the bottom of the page.
21660     onRender : function(ct, position)
21661     {
21662         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21663         
21664         
21665         
21666         if(!this.el){
21667             var cfg = Roo.apply({},  this.getAutoCreate());
21668             cfg.id = Roo.id();
21669             
21670             if (this.cls) {
21671                 cfg.cls += ' ' + this.cls;
21672             }
21673             if (this.style) {
21674                 cfg.style = this.style;
21675             }
21676             //Roo.log("adding to ");
21677             this.el = Roo.get(document.body).createChild(cfg, position);
21678 //            Roo.log(this.el);
21679         }
21680         
21681         this.contentEl = this.el.select('.popover-content',true).first();
21682         this.headerEl =  this.el.select('.popover-title',true).first();
21683         
21684         var nitems = [];
21685         if(typeof(this.items) != 'undefined'){
21686             var items = this.items;
21687             delete this.items;
21688
21689             for(var i =0;i < items.length;i++) {
21690                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21691             }
21692         }
21693
21694         this.items = nitems;
21695         
21696         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21697         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21698         
21699         
21700         
21701         this.initEvents();
21702     },
21703     
21704     resizeMask : function()
21705     {
21706         this.maskEl.setSize(
21707             Roo.lib.Dom.getViewWidth(true),
21708             Roo.lib.Dom.getViewHeight(true)
21709         );
21710     },
21711     
21712     initEvents : function()
21713     {
21714         
21715         if (!this.modal) { 
21716             Roo.bootstrap.Popover.register(this);
21717         }
21718          
21719         this.arrowEl = this.el.select('.arrow',true).first();
21720         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21721         this.el.enableDisplayMode('block');
21722         this.el.hide();
21723  
21724         
21725         if (this.over === false && !this.parent()) {
21726             return; 
21727         }
21728         if (this.triggers === false) {
21729             return;
21730         }
21731          
21732         // support parent
21733         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21734         var triggers = this.trigger ? this.trigger.split(' ') : [];
21735         Roo.each(triggers, function(trigger) {
21736         
21737             if (trigger == 'click') {
21738                 on_el.on('click', this.toggle, this);
21739             } else if (trigger != 'manual') {
21740                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21741                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21742       
21743                 on_el.on(eventIn  ,this.enter, this);
21744                 on_el.on(eventOut, this.leave, this);
21745             }
21746         }, this);
21747     },
21748     
21749     
21750     // private
21751     timeout : null,
21752     hoverState : null,
21753     
21754     toggle : function () {
21755         this.hoverState == 'in' ? this.leave() : this.enter();
21756     },
21757     
21758     enter : function () {
21759         
21760         clearTimeout(this.timeout);
21761     
21762         this.hoverState = 'in';
21763     
21764         if (!this.delay || !this.delay.show) {
21765             this.show();
21766             return;
21767         }
21768         var _t = this;
21769         this.timeout = setTimeout(function () {
21770             if (_t.hoverState == 'in') {
21771                 _t.show();
21772             }
21773         }, this.delay.show)
21774     },
21775     
21776     leave : function() {
21777         clearTimeout(this.timeout);
21778     
21779         this.hoverState = 'out';
21780     
21781         if (!this.delay || !this.delay.hide) {
21782             this.hide();
21783             return;
21784         }
21785         var _t = this;
21786         this.timeout = setTimeout(function () {
21787             if (_t.hoverState == 'out') {
21788                 _t.hide();
21789             }
21790         }, this.delay.hide)
21791     },
21792     
21793     /**
21794      * update the position of the dialog
21795      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21796      * 
21797      *
21798      */
21799     
21800     doAlign : function()
21801     {
21802         
21803         if (this.alignEl) {
21804             this.updatePosition(this.placement, true);
21805              
21806         } else {
21807             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21808             var es = this.el.getSize();
21809             var x = Roo.lib.Dom.getViewWidth()/2;
21810             var y = Roo.lib.Dom.getViewHeight()/2;
21811             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21812             
21813         }
21814
21815          
21816          
21817         
21818         
21819     },
21820     
21821     /**
21822      * Show the popover
21823      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21824      * @param {string} (left|right|top|bottom) position
21825      */
21826     show : function (on_el, placement)
21827     {
21828         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21829         on_el = on_el || false; // default to false
21830          
21831         if (!on_el) {
21832             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21833                 on_el = this.parent().el;
21834             } else if (this.over) {
21835                 on_el = Roo.get(this.over);
21836             }
21837             
21838         }
21839         
21840         this.alignEl = Roo.get( on_el );
21841
21842         if (!this.el) {
21843             this.render(document.body);
21844         }
21845         
21846         
21847          
21848         
21849         if (this.title === false) {
21850             this.headerEl.hide();
21851         }
21852         
21853        
21854         this.el.show();
21855         this.el.dom.style.display = 'block';
21856          
21857         this.doAlign();
21858         
21859         //var arrow = this.el.select('.arrow',true).first();
21860         //arrow.set(align[2], 
21861         
21862         this.el.addClass('in');
21863         
21864          
21865         
21866         this.hoverState = 'in';
21867         
21868         if (this.modal) {
21869             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21870             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21871             this.maskEl.dom.style.display = 'block';
21872             this.maskEl.addClass('show');
21873         }
21874         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21875  
21876         this.fireEvent('show', this);
21877         
21878     },
21879     /**
21880      * fire this manually after loading a grid in the table for example
21881      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21882      * @param {Boolean} try and move it if we cant get right position.
21883      */
21884     updatePosition : function(placement, try_move)
21885     {
21886         // allow for calling with no parameters
21887         placement = placement   ? placement :  this.placement;
21888         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21889         
21890         this.el.removeClass([
21891             'fade','top','bottom', 'left', 'right','in',
21892             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21893         ]);
21894         this.el.addClass(placement + ' bs-popover-' + placement);
21895         
21896         if (!this.alignEl ) {
21897             return false;
21898         }
21899         
21900         switch (placement) {
21901             case 'right':
21902                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21903                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21904                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21905                     //normal display... or moved up/down.
21906                     this.el.setXY(offset);
21907                     var xy = this.alignEl.getAnchorXY('tr', false);
21908                     xy[0]+=2;xy[1]+=5;
21909                     this.arrowEl.setXY(xy);
21910                     return true;
21911                 }
21912                 // continue through...
21913                 return this.updatePosition('left', false);
21914                 
21915             
21916             case 'left':
21917                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21918                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21919                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21920                     //normal display... or moved up/down.
21921                     this.el.setXY(offset);
21922                     var xy = this.alignEl.getAnchorXY('tl', false);
21923                     xy[0]-=10;xy[1]+=5; // << fix me
21924                     this.arrowEl.setXY(xy);
21925                     return true;
21926                 }
21927                 // call self...
21928                 return this.updatePosition('right', false);
21929             
21930             case 'top':
21931                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21932                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21933                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21934                     //normal display... or moved up/down.
21935                     this.el.setXY(offset);
21936                     var xy = this.alignEl.getAnchorXY('t', false);
21937                     xy[1]-=10; // << fix me
21938                     this.arrowEl.setXY(xy);
21939                     return true;
21940                 }
21941                 // fall through
21942                return this.updatePosition('bottom', false);
21943             
21944             case 'bottom':
21945                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21946                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21947                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21948                     //normal display... or moved up/down.
21949                     this.el.setXY(offset);
21950                     var xy = this.alignEl.getAnchorXY('b', false);
21951                      xy[1]+=2; // << fix me
21952                     this.arrowEl.setXY(xy);
21953                     return true;
21954                 }
21955                 // fall through
21956                 return this.updatePosition('top', false);
21957                 
21958             
21959         }
21960         
21961         
21962         return false;
21963     },
21964     
21965     hide : function()
21966     {
21967         this.el.setXY([0,0]);
21968         this.el.removeClass('in');
21969         this.el.hide();
21970         this.hoverState = null;
21971         this.maskEl.hide(); // always..
21972         this.fireEvent('hide', this);
21973     }
21974     
21975 });
21976
21977
21978 Roo.apply(Roo.bootstrap.Popover, {
21979
21980     alignment : {
21981         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21982         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21983         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21984         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21985     },
21986     
21987     zIndex : 20001,
21988
21989     clickHander : false,
21990     
21991     
21992
21993     onMouseDown : function(e)
21994     {
21995         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21996             /// what is nothing is showing..
21997             this.hideAll();
21998         }
21999          
22000     },
22001     
22002     
22003     popups : [],
22004     
22005     register : function(popup)
22006     {
22007         if (!Roo.bootstrap.Popover.clickHandler) {
22008             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22009         }
22010         // hide other popups.
22011         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
22012         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
22013         this.hideAll(); //<< why?
22014         //this.popups.push(popup);
22015     },
22016     hideAll : function()
22017     {
22018         this.popups.forEach(function(p) {
22019             p.hide();
22020         });
22021     },
22022     onShow : function() {
22023         Roo.bootstrap.Popover.popups.push(this);
22024     },
22025     onHide : function() {
22026         Roo.bootstrap.Popover.popups.remove(this);
22027     } 
22028
22029 });
22030 /**
22031  * @class Roo.bootstrap.PopoverNav
22032  * @extends Roo.bootstrap.nav.Simplebar
22033  * @parent Roo.bootstrap.Popover
22034  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22035  * @licence LGPL
22036  * Bootstrap Popover header navigation class
22037  * FIXME? should this go under nav?
22038  *
22039  * 
22040  * @constructor
22041  * Create a new Popover Header Navigation 
22042  * @param {Object} config The config object
22043  */
22044
22045 Roo.bootstrap.PopoverNav = function(config){
22046     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22047 };
22048
22049 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22050     
22051     
22052     container_method : 'getPopoverHeader' 
22053     
22054      
22055     
22056     
22057    
22058 });
22059
22060  
22061
22062  /*
22063  * - LGPL
22064  *
22065  * Progress
22066  * 
22067  */
22068
22069 /**
22070  * @class Roo.bootstrap.Progress
22071  * @extends Roo.bootstrap.Component
22072  * @children Roo.bootstrap.ProgressBar
22073  * Bootstrap Progress class
22074  * @cfg {Boolean} striped striped of the progress bar
22075  * @cfg {Boolean} active animated of the progress bar
22076  * 
22077  * 
22078  * @constructor
22079  * Create a new Progress
22080  * @param {Object} config The config object
22081  */
22082
22083 Roo.bootstrap.Progress = function(config){
22084     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22085 };
22086
22087 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22088     
22089     striped : false,
22090     active: false,
22091     
22092     getAutoCreate : function(){
22093         var cfg = {
22094             tag: 'div',
22095             cls: 'progress'
22096         };
22097         
22098         
22099         if(this.striped){
22100             cfg.cls += ' progress-striped';
22101         }
22102       
22103         if(this.active){
22104             cfg.cls += ' active';
22105         }
22106         
22107         
22108         return cfg;
22109     }
22110    
22111 });
22112
22113  
22114
22115  /*
22116  * - LGPL
22117  *
22118  * ProgressBar
22119  * 
22120  */
22121
22122 /**
22123  * @class Roo.bootstrap.ProgressBar
22124  * @extends Roo.bootstrap.Component
22125  * Bootstrap ProgressBar class
22126  * @cfg {Number} aria_valuenow aria-value now
22127  * @cfg {Number} aria_valuemin aria-value min
22128  * @cfg {Number} aria_valuemax aria-value max
22129  * @cfg {String} label label for the progress bar
22130  * @cfg {String} panel (success | info | warning | danger )
22131  * @cfg {String} role role of the progress bar
22132  * @cfg {String} sr_only text
22133  * 
22134  * 
22135  * @constructor
22136  * Create a new ProgressBar
22137  * @param {Object} config The config object
22138  */
22139
22140 Roo.bootstrap.ProgressBar = function(config){
22141     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22142 };
22143
22144 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22145     
22146     aria_valuenow : 0,
22147     aria_valuemin : 0,
22148     aria_valuemax : 100,
22149     label : false,
22150     panel : false,
22151     role : false,
22152     sr_only: false,
22153     
22154     getAutoCreate : function()
22155     {
22156         
22157         var cfg = {
22158             tag: 'div',
22159             cls: 'progress-bar',
22160             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22161         };
22162         
22163         if(this.sr_only){
22164             cfg.cn = {
22165                 tag: 'span',
22166                 cls: 'sr-only',
22167                 html: this.sr_only
22168             }
22169         }
22170         
22171         if(this.role){
22172             cfg.role = this.role;
22173         }
22174         
22175         if(this.aria_valuenow){
22176             cfg['aria-valuenow'] = this.aria_valuenow;
22177         }
22178         
22179         if(this.aria_valuemin){
22180             cfg['aria-valuemin'] = this.aria_valuemin;
22181         }
22182         
22183         if(this.aria_valuemax){
22184             cfg['aria-valuemax'] = this.aria_valuemax;
22185         }
22186         
22187         if(this.label && !this.sr_only){
22188             cfg.html = this.label;
22189         }
22190         
22191         if(this.panel){
22192             cfg.cls += ' progress-bar-' + this.panel;
22193         }
22194         
22195         return cfg;
22196     },
22197     
22198     update : function(aria_valuenow)
22199     {
22200         this.aria_valuenow = aria_valuenow;
22201         
22202         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22203     }
22204    
22205 });
22206
22207  
22208
22209  /**
22210  * @class Roo.bootstrap.TabGroup
22211  * @extends Roo.bootstrap.Column
22212  * @children Roo.bootstrap.TabPanel
22213  * Bootstrap Column class
22214  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22215  * @cfg {Boolean} carousel true to make the group behave like a carousel
22216  * @cfg {Boolean} bullets show bullets for the panels
22217  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22218  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22219  * @cfg {Boolean} showarrow (true|false) show arrow default true
22220  * 
22221  * @constructor
22222  * Create a new TabGroup
22223  * @param {Object} config The config object
22224  */
22225
22226 Roo.bootstrap.TabGroup = function(config){
22227     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22228     if (!this.navId) {
22229         this.navId = Roo.id();
22230     }
22231     this.tabs = [];
22232     Roo.bootstrap.TabGroup.register(this);
22233     
22234 };
22235
22236 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22237     
22238     carousel : false,
22239     transition : false,
22240     bullets : 0,
22241     timer : 0,
22242     autoslide : false,
22243     slideFn : false,
22244     slideOnTouch : false,
22245     showarrow : true,
22246     
22247     getAutoCreate : function()
22248     {
22249         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22250         
22251         cfg.cls += ' tab-content';
22252         
22253         if (this.carousel) {
22254             cfg.cls += ' carousel slide';
22255             
22256             cfg.cn = [{
22257                cls : 'carousel-inner',
22258                cn : []
22259             }];
22260         
22261             if(this.bullets  && !Roo.isTouch){
22262                 
22263                 var bullets = {
22264                     cls : 'carousel-bullets',
22265                     cn : []
22266                 };
22267                
22268                 if(this.bullets_cls){
22269                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22270                 }
22271                 
22272                 bullets.cn.push({
22273                     cls : 'clear'
22274                 });
22275                 
22276                 cfg.cn[0].cn.push(bullets);
22277             }
22278             
22279             if(this.showarrow){
22280                 cfg.cn[0].cn.push({
22281                     tag : 'div',
22282                     class : 'carousel-arrow',
22283                     cn : [
22284                         {
22285                             tag : 'div',
22286                             class : 'carousel-prev',
22287                             cn : [
22288                                 {
22289                                     tag : 'i',
22290                                     class : 'fa fa-chevron-left'
22291                                 }
22292                             ]
22293                         },
22294                         {
22295                             tag : 'div',
22296                             class : 'carousel-next',
22297                             cn : [
22298                                 {
22299                                     tag : 'i',
22300                                     class : 'fa fa-chevron-right'
22301                                 }
22302                             ]
22303                         }
22304                     ]
22305                 });
22306             }
22307             
22308         }
22309         
22310         return cfg;
22311     },
22312     
22313     initEvents:  function()
22314     {
22315 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22316 //            this.el.on("touchstart", this.onTouchStart, this);
22317 //        }
22318         
22319         if(this.autoslide){
22320             var _this = this;
22321             
22322             this.slideFn = window.setInterval(function() {
22323                 _this.showPanelNext();
22324             }, this.timer);
22325         }
22326         
22327         if(this.showarrow){
22328             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22329             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22330         }
22331         
22332         
22333     },
22334     
22335 //    onTouchStart : function(e, el, o)
22336 //    {
22337 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22338 //            return;
22339 //        }
22340 //        
22341 //        this.showPanelNext();
22342 //    },
22343     
22344     
22345     getChildContainer : function()
22346     {
22347         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22348     },
22349     
22350     /**
22351     * register a Navigation item
22352     * @param {Roo.bootstrap.nav.Item} the navitem to add
22353     */
22354     register : function(item)
22355     {
22356         this.tabs.push( item);
22357         item.navId = this.navId; // not really needed..
22358         this.addBullet();
22359     
22360     },
22361     
22362     getActivePanel : function()
22363     {
22364         var r = false;
22365         Roo.each(this.tabs, function(t) {
22366             if (t.active) {
22367                 r = t;
22368                 return false;
22369             }
22370             return null;
22371         });
22372         return r;
22373         
22374     },
22375     getPanelByName : function(n)
22376     {
22377         var r = false;
22378         Roo.each(this.tabs, function(t) {
22379             if (t.tabId == n) {
22380                 r = t;
22381                 return false;
22382             }
22383             return null;
22384         });
22385         return r;
22386     },
22387     indexOfPanel : function(p)
22388     {
22389         var r = false;
22390         Roo.each(this.tabs, function(t,i) {
22391             if (t.tabId == p.tabId) {
22392                 r = i;
22393                 return false;
22394             }
22395             return null;
22396         });
22397         return r;
22398     },
22399     /**
22400      * show a specific panel
22401      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22402      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22403      */
22404     showPanel : function (pan)
22405     {
22406         if(this.transition || typeof(pan) == 'undefined'){
22407             Roo.log("waiting for the transitionend");
22408             return false;
22409         }
22410         
22411         if (typeof(pan) == 'number') {
22412             pan = this.tabs[pan];
22413         }
22414         
22415         if (typeof(pan) == 'string') {
22416             pan = this.getPanelByName(pan);
22417         }
22418         
22419         var cur = this.getActivePanel();
22420         
22421         if(!pan || !cur){
22422             Roo.log('pan or acitve pan is undefined');
22423             return false;
22424         }
22425         
22426         if (pan.tabId == this.getActivePanel().tabId) {
22427             return true;
22428         }
22429         
22430         if (false === cur.fireEvent('beforedeactivate')) {
22431             return false;
22432         }
22433         
22434         if(this.bullets > 0 && !Roo.isTouch){
22435             this.setActiveBullet(this.indexOfPanel(pan));
22436         }
22437         
22438         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22439             
22440             //class="carousel-item carousel-item-next carousel-item-left"
22441             
22442             this.transition = true;
22443             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22444             var lr = dir == 'next' ? 'left' : 'right';
22445             pan.el.addClass(dir); // or prev
22446             pan.el.addClass('carousel-item-' + dir); // or prev
22447             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22448             cur.el.addClass(lr); // or right
22449             pan.el.addClass(lr);
22450             cur.el.addClass('carousel-item-' +lr); // or right
22451             pan.el.addClass('carousel-item-' +lr);
22452             
22453             
22454             var _this = this;
22455             cur.el.on('transitionend', function() {
22456                 Roo.log("trans end?");
22457                 
22458                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22459                 pan.setActive(true);
22460                 
22461                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22462                 cur.setActive(false);
22463                 
22464                 _this.transition = false;
22465                 
22466             }, this, { single:  true } );
22467             
22468             return true;
22469         }
22470         
22471         cur.setActive(false);
22472         pan.setActive(true);
22473         
22474         return true;
22475         
22476     },
22477     showPanelNext : function()
22478     {
22479         var i = this.indexOfPanel(this.getActivePanel());
22480         
22481         if (i >= this.tabs.length - 1 && !this.autoslide) {
22482             return;
22483         }
22484         
22485         if (i >= this.tabs.length - 1 && this.autoslide) {
22486             i = -1;
22487         }
22488         
22489         this.showPanel(this.tabs[i+1]);
22490     },
22491     
22492     showPanelPrev : function()
22493     {
22494         var i = this.indexOfPanel(this.getActivePanel());
22495         
22496         if (i  < 1 && !this.autoslide) {
22497             return;
22498         }
22499         
22500         if (i < 1 && this.autoslide) {
22501             i = this.tabs.length;
22502         }
22503         
22504         this.showPanel(this.tabs[i-1]);
22505     },
22506     
22507     
22508     addBullet: function()
22509     {
22510         if(!this.bullets || Roo.isTouch){
22511             return;
22512         }
22513         var ctr = this.el.select('.carousel-bullets',true).first();
22514         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22515         var bullet = ctr.createChild({
22516             cls : 'bullet bullet-' + i
22517         },ctr.dom.lastChild);
22518         
22519         
22520         var _this = this;
22521         
22522         bullet.on('click', (function(e, el, o, ii, t){
22523
22524             e.preventDefault();
22525
22526             this.showPanel(ii);
22527
22528             if(this.autoslide && this.slideFn){
22529                 clearInterval(this.slideFn);
22530                 this.slideFn = window.setInterval(function() {
22531                     _this.showPanelNext();
22532                 }, this.timer);
22533             }
22534
22535         }).createDelegate(this, [i, bullet], true));
22536                 
22537         
22538     },
22539      
22540     setActiveBullet : function(i)
22541     {
22542         if(Roo.isTouch){
22543             return;
22544         }
22545         
22546         Roo.each(this.el.select('.bullet', true).elements, function(el){
22547             el.removeClass('selected');
22548         });
22549
22550         var bullet = this.el.select('.bullet-' + i, true).first();
22551         
22552         if(!bullet){
22553             return;
22554         }
22555         
22556         bullet.addClass('selected');
22557     }
22558     
22559     
22560   
22561 });
22562
22563  
22564
22565  
22566  
22567 Roo.apply(Roo.bootstrap.TabGroup, {
22568     
22569     groups: {},
22570      /**
22571     * register a Navigation Group
22572     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22573     */
22574     register : function(navgrp)
22575     {
22576         this.groups[navgrp.navId] = navgrp;
22577         
22578     },
22579     /**
22580     * fetch a Navigation Group based on the navigation ID
22581     * if one does not exist , it will get created.
22582     * @param {string} the navgroup to add
22583     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22584     */
22585     get: function(navId) {
22586         if (typeof(this.groups[navId]) == 'undefined') {
22587             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22588         }
22589         return this.groups[navId] ;
22590     }
22591     
22592     
22593     
22594 });
22595
22596  /*
22597  * - LGPL
22598  *
22599  * TabPanel
22600  * 
22601  */
22602
22603 /**
22604  * @class Roo.bootstrap.TabPanel
22605  * @extends Roo.bootstrap.Component
22606  * @children Roo.bootstrap.Component
22607  * Bootstrap TabPanel class
22608  * @cfg {Boolean} active panel active
22609  * @cfg {String} html panel content
22610  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22611  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22612  * @cfg {String} href click to link..
22613  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22614  * 
22615  * 
22616  * @constructor
22617  * Create a new TabPanel
22618  * @param {Object} config The config object
22619  */
22620
22621 Roo.bootstrap.TabPanel = function(config){
22622     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22623     this.addEvents({
22624         /**
22625              * @event changed
22626              * Fires when the active status changes
22627              * @param {Roo.bootstrap.TabPanel} this
22628              * @param {Boolean} state the new state
22629             
22630          */
22631         'changed': true,
22632         /**
22633              * @event beforedeactivate
22634              * Fires before a tab is de-activated - can be used to do validation on a form.
22635              * @param {Roo.bootstrap.TabPanel} this
22636              * @return {Boolean} false if there is an error
22637             
22638          */
22639         'beforedeactivate': true
22640      });
22641     
22642     this.tabId = this.tabId || Roo.id();
22643   
22644 };
22645
22646 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22647     
22648     active: false,
22649     html: false,
22650     tabId: false,
22651     navId : false,
22652     href : '',
22653     touchSlide : false,
22654     getAutoCreate : function(){
22655         
22656         
22657         var cfg = {
22658             tag: 'div',
22659             // item is needed for carousel - not sure if it has any effect otherwise
22660             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22661             html: this.html || ''
22662         };
22663         
22664         if(this.active){
22665             cfg.cls += ' active';
22666         }
22667         
22668         if(this.tabId){
22669             cfg.tabId = this.tabId;
22670         }
22671         
22672         
22673         
22674         return cfg;
22675     },
22676     
22677     initEvents:  function()
22678     {
22679         var p = this.parent();
22680         
22681         this.navId = this.navId || p.navId;
22682         
22683         if (typeof(this.navId) != 'undefined') {
22684             // not really needed.. but just in case.. parent should be a NavGroup.
22685             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22686             
22687             tg.register(this);
22688             
22689             var i = tg.tabs.length - 1;
22690             
22691             if(this.active && tg.bullets > 0 && i < tg.bullets){
22692                 tg.setActiveBullet(i);
22693             }
22694         }
22695         
22696         this.el.on('click', this.onClick, this);
22697         
22698         if(Roo.isTouch && this.touchSlide){
22699             this.el.on("touchstart", this.onTouchStart, this);
22700             this.el.on("touchmove", this.onTouchMove, this);
22701             this.el.on("touchend", this.onTouchEnd, this);
22702         }
22703         
22704     },
22705     
22706     onRender : function(ct, position)
22707     {
22708         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22709     },
22710     
22711     setActive : function(state)
22712     {
22713         Roo.log("panel - set active " + this.tabId + "=" + state);
22714         
22715         this.active = state;
22716         if (!state) {
22717             this.el.removeClass('active');
22718             
22719         } else  if (!this.el.hasClass('active')) {
22720             this.el.addClass('active');
22721         }
22722         
22723         this.fireEvent('changed', this, state);
22724     },
22725     
22726     onClick : function(e)
22727     {
22728         e.preventDefault();
22729         
22730         if(!this.href.length){
22731             return;
22732         }
22733         
22734         window.location.href = this.href;
22735     },
22736     
22737     startX : 0,
22738     startY : 0,
22739     endX : 0,
22740     endY : 0,
22741     swiping : false,
22742     
22743     onTouchStart : function(e)
22744     {
22745         this.swiping = false;
22746         
22747         this.startX = e.browserEvent.touches[0].clientX;
22748         this.startY = e.browserEvent.touches[0].clientY;
22749     },
22750     
22751     onTouchMove : function(e)
22752     {
22753         this.swiping = true;
22754         
22755         this.endX = e.browserEvent.touches[0].clientX;
22756         this.endY = e.browserEvent.touches[0].clientY;
22757     },
22758     
22759     onTouchEnd : function(e)
22760     {
22761         if(!this.swiping){
22762             this.onClick(e);
22763             return;
22764         }
22765         
22766         var tabGroup = this.parent();
22767         
22768         if(this.endX > this.startX){ // swiping right
22769             tabGroup.showPanelPrev();
22770             return;
22771         }
22772         
22773         if(this.startX > this.endX){ // swiping left
22774             tabGroup.showPanelNext();
22775             return;
22776         }
22777     }
22778     
22779     
22780 });
22781  
22782
22783  
22784
22785  /*
22786  * - LGPL
22787  *
22788  * DateField
22789  * 
22790  */
22791
22792 /**
22793  * @class Roo.bootstrap.form.DateField
22794  * @extends Roo.bootstrap.form.Input
22795  * Bootstrap DateField class
22796  * @cfg {Number} weekStart default 0
22797  * @cfg {String} viewMode default empty, (months|years)
22798  * @cfg {String} minViewMode default empty, (months|years)
22799  * @cfg {Number} startDate default -Infinity
22800  * @cfg {Number} endDate default Infinity
22801  * @cfg {Boolean} todayHighlight default false
22802  * @cfg {Boolean} todayBtn default false
22803  * @cfg {Boolean} calendarWeeks default false
22804  * @cfg {Object} daysOfWeekDisabled default empty
22805  * @cfg {Boolean} singleMode default false (true | false)
22806  * 
22807  * @cfg {Boolean} keyboardNavigation default true
22808  * @cfg {String} language default en
22809  * 
22810  * @constructor
22811  * Create a new DateField
22812  * @param {Object} config The config object
22813  */
22814
22815 Roo.bootstrap.form.DateField = function(config){
22816     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22817      this.addEvents({
22818             /**
22819              * @event show
22820              * Fires when this field show.
22821              * @param {Roo.bootstrap.form.DateField} this
22822              * @param {Mixed} date The date value
22823              */
22824             show : true,
22825             /**
22826              * @event show
22827              * Fires when this field hide.
22828              * @param {Roo.bootstrap.form.DateField} this
22829              * @param {Mixed} date The date value
22830              */
22831             hide : true,
22832             /**
22833              * @event select
22834              * Fires when select a date.
22835              * @param {Roo.bootstrap.form.DateField} this
22836              * @param {Mixed} date The date value
22837              */
22838             select : true,
22839             /**
22840              * @event beforeselect
22841              * Fires when before select a date.
22842              * @param {Roo.bootstrap.form.DateField} this
22843              * @param {Mixed} date The date value
22844              */
22845             beforeselect : true
22846         });
22847 };
22848
22849 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22850     
22851     /**
22852      * @cfg {String} format
22853      * The default date format string which can be overriden for localization support.  The format must be
22854      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22855      */
22856     format : "m/d/y",
22857     /**
22858      * @cfg {String} altFormats
22859      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22860      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22861      */
22862     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22863     
22864     weekStart : 0,
22865     
22866     viewMode : '',
22867     
22868     minViewMode : '',
22869     
22870     todayHighlight : false,
22871     
22872     todayBtn: false,
22873     
22874     language: 'en',
22875     
22876     keyboardNavigation: true,
22877     
22878     calendarWeeks: false,
22879     
22880     startDate: -Infinity,
22881     
22882     endDate: Infinity,
22883     
22884     daysOfWeekDisabled: [],
22885     
22886     _events: [],
22887     
22888     singleMode : false,
22889     
22890     UTCDate: function()
22891     {
22892         return new Date(Date.UTC.apply(Date, arguments));
22893     },
22894     
22895     UTCToday: function()
22896     {
22897         var today = new Date();
22898         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22899     },
22900     
22901     getDate: function() {
22902             var d = this.getUTCDate();
22903             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22904     },
22905     
22906     getUTCDate: function() {
22907             return this.date;
22908     },
22909     
22910     setDate: function(d) {
22911             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22912     },
22913     
22914     setUTCDate: function(d) {
22915             this.date = d;
22916             this.setValue(this.formatDate(this.date));
22917     },
22918         
22919     onRender: function(ct, position)
22920     {
22921         
22922         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22923         
22924         this.language = this.language || 'en';
22925         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22926         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22927         
22928         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22929         this.format = this.format || 'm/d/y';
22930         this.isInline = false;
22931         this.isInput = true;
22932         this.component = this.el.select('.add-on', true).first() || false;
22933         this.component = (this.component && this.component.length === 0) ? false : this.component;
22934         this.hasInput = this.component && this.inputEl().length;
22935         
22936         if (typeof(this.minViewMode === 'string')) {
22937             switch (this.minViewMode) {
22938                 case 'months':
22939                     this.minViewMode = 1;
22940                     break;
22941                 case 'years':
22942                     this.minViewMode = 2;
22943                     break;
22944                 default:
22945                     this.minViewMode = 0;
22946                     break;
22947             }
22948         }
22949         
22950         if (typeof(this.viewMode === 'string')) {
22951             switch (this.viewMode) {
22952                 case 'months':
22953                     this.viewMode = 1;
22954                     break;
22955                 case 'years':
22956                     this.viewMode = 2;
22957                     break;
22958                 default:
22959                     this.viewMode = 0;
22960                     break;
22961             }
22962         }
22963                 
22964         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22965         
22966 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22967         
22968         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22969         
22970         this.picker().on('mousedown', this.onMousedown, this);
22971         this.picker().on('click', this.onClick, this);
22972         
22973         this.picker().addClass('datepicker-dropdown');
22974         
22975         this.startViewMode = this.viewMode;
22976         
22977         if(this.singleMode){
22978             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22979                 v.setVisibilityMode(Roo.Element.DISPLAY);
22980                 v.hide();
22981             });
22982             
22983             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22984                 v.setStyle('width', '189px');
22985             });
22986         }
22987         
22988         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22989             if(!this.calendarWeeks){
22990                 v.remove();
22991                 return;
22992             }
22993             
22994             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22995             v.attr('colspan', function(i, val){
22996                 return parseInt(val) + 1;
22997             });
22998         });
22999                         
23000         
23001         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23002         
23003         this.setStartDate(this.startDate);
23004         this.setEndDate(this.endDate);
23005         
23006         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23007         
23008         this.fillDow();
23009         this.fillMonths();
23010         this.update();
23011         this.showMode();
23012         
23013         if(this.isInline) {
23014             this.showPopup();
23015         }
23016     },
23017     
23018     picker : function()
23019     {
23020         return this.pickerEl;
23021 //        return this.el.select('.datepicker', true).first();
23022     },
23023     
23024     fillDow: function()
23025     {
23026         var dowCnt = this.weekStart;
23027         
23028         var dow = {
23029             tag: 'tr',
23030             cn: [
23031                 
23032             ]
23033         };
23034         
23035         if(this.calendarWeeks){
23036             dow.cn.push({
23037                 tag: 'th',
23038                 cls: 'cw',
23039                 html: '&nbsp;'
23040             })
23041         }
23042         
23043         while (dowCnt < this.weekStart + 7) {
23044             dow.cn.push({
23045                 tag: 'th',
23046                 cls: 'dow',
23047                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23048             });
23049         }
23050         
23051         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23052     },
23053     
23054     fillMonths: function()
23055     {    
23056         var i = 0;
23057         var months = this.picker().select('>.datepicker-months td', true).first();
23058         
23059         months.dom.innerHTML = '';
23060         
23061         while (i < 12) {
23062             var month = {
23063                 tag: 'span',
23064                 cls: 'month',
23065                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23066             };
23067             
23068             months.createChild(month);
23069         }
23070         
23071     },
23072     
23073     update: function()
23074     {
23075         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;
23076         
23077         if (this.date < this.startDate) {
23078             this.viewDate = new Date(this.startDate);
23079         } else if (this.date > this.endDate) {
23080             this.viewDate = new Date(this.endDate);
23081         } else {
23082             this.viewDate = new Date(this.date);
23083         }
23084         
23085         this.fill();
23086     },
23087     
23088     fill: function() 
23089     {
23090         var d = new Date(this.viewDate),
23091                 year = d.getUTCFullYear(),
23092                 month = d.getUTCMonth(),
23093                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23094                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23095                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23096                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23097                 currentDate = this.date && this.date.valueOf(),
23098                 today = this.UTCToday();
23099         
23100         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23101         
23102 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23103         
23104 //        this.picker.select('>tfoot th.today').
23105 //                                              .text(dates[this.language].today)
23106 //                                              .toggle(this.todayBtn !== false);
23107     
23108         this.updateNavArrows();
23109         this.fillMonths();
23110                                                 
23111         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23112         
23113         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23114          
23115         prevMonth.setUTCDate(day);
23116         
23117         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23118         
23119         var nextMonth = new Date(prevMonth);
23120         
23121         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23122         
23123         nextMonth = nextMonth.valueOf();
23124         
23125         var fillMonths = false;
23126         
23127         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23128         
23129         while(prevMonth.valueOf() <= nextMonth) {
23130             var clsName = '';
23131             
23132             if (prevMonth.getUTCDay() === this.weekStart) {
23133                 if(fillMonths){
23134                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23135                 }
23136                     
23137                 fillMonths = {
23138                     tag: 'tr',
23139                     cn: []
23140                 };
23141                 
23142                 if(this.calendarWeeks){
23143                     // ISO 8601: First week contains first thursday.
23144                     // ISO also states week starts on Monday, but we can be more abstract here.
23145                     var
23146                     // Start of current week: based on weekstart/current date
23147                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23148                     // Thursday of this week
23149                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23150                     // First Thursday of year, year from thursday
23151                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23152                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23153                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23154                     
23155                     fillMonths.cn.push({
23156                         tag: 'td',
23157                         cls: 'cw',
23158                         html: calWeek
23159                     });
23160                 }
23161             }
23162             
23163             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23164                 clsName += ' old';
23165             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23166                 clsName += ' new';
23167             }
23168             if (this.todayHighlight &&
23169                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23170                 prevMonth.getUTCMonth() == today.getMonth() &&
23171                 prevMonth.getUTCDate() == today.getDate()) {
23172                 clsName += ' today';
23173             }
23174             
23175             if (currentDate && prevMonth.valueOf() === currentDate) {
23176                 clsName += ' active';
23177             }
23178             
23179             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23180                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23181                     clsName += ' disabled';
23182             }
23183             
23184             fillMonths.cn.push({
23185                 tag: 'td',
23186                 cls: 'day ' + clsName,
23187                 html: prevMonth.getDate()
23188             });
23189             
23190             prevMonth.setDate(prevMonth.getDate()+1);
23191         }
23192           
23193         var currentYear = this.date && this.date.getUTCFullYear();
23194         var currentMonth = this.date && this.date.getUTCMonth();
23195         
23196         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23197         
23198         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23199             v.removeClass('active');
23200             
23201             if(currentYear === year && k === currentMonth){
23202                 v.addClass('active');
23203             }
23204             
23205             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23206                 v.addClass('disabled');
23207             }
23208             
23209         });
23210         
23211         
23212         year = parseInt(year/10, 10) * 10;
23213         
23214         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23215         
23216         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23217         
23218         year -= 1;
23219         for (var i = -1; i < 11; i++) {
23220             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23221                 tag: 'span',
23222                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23223                 html: year
23224             });
23225             
23226             year += 1;
23227         }
23228     },
23229     
23230     showMode: function(dir) 
23231     {
23232         if (dir) {
23233             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23234         }
23235         
23236         Roo.each(this.picker().select('>div',true).elements, function(v){
23237             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23238             v.hide();
23239         });
23240         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23241     },
23242     
23243     place: function()
23244     {
23245         if(this.isInline) {
23246             return;
23247         }
23248         
23249         this.picker().removeClass(['bottom', 'top']);
23250         
23251         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23252             /*
23253              * place to the top of element!
23254              *
23255              */
23256             
23257             this.picker().addClass('top');
23258             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23259             
23260             return;
23261         }
23262         
23263         this.picker().addClass('bottom');
23264         
23265         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23266     },
23267     
23268     parseDate : function(value)
23269     {
23270         if(!value || value instanceof Date){
23271             return value;
23272         }
23273         var v = Date.parseDate(value, this.format);
23274         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23275             v = Date.parseDate(value, 'Y-m-d');
23276         }
23277         if(!v && this.altFormats){
23278             if(!this.altFormatsArray){
23279                 this.altFormatsArray = this.altFormats.split("|");
23280             }
23281             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23282                 v = Date.parseDate(value, this.altFormatsArray[i]);
23283             }
23284         }
23285         return v;
23286     },
23287     
23288     formatDate : function(date, fmt)
23289     {   
23290         return (!date || !(date instanceof Date)) ?
23291         date : date.dateFormat(fmt || this.format);
23292     },
23293     
23294     onFocus : function()
23295     {
23296         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23297         this.showPopup();
23298     },
23299     
23300     onBlur : function()
23301     {
23302         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23303         
23304         var d = this.inputEl().getValue();
23305         
23306         this.setValue(d);
23307                 
23308         this.hidePopup();
23309     },
23310     
23311     showPopup : function()
23312     {
23313         this.picker().show();
23314         this.update();
23315         this.place();
23316         
23317         this.fireEvent('showpopup', this, this.date);
23318     },
23319     
23320     hidePopup : function()
23321     {
23322         if(this.isInline) {
23323             return;
23324         }
23325         this.picker().hide();
23326         this.viewMode = this.startViewMode;
23327         this.showMode();
23328         
23329         this.fireEvent('hidepopup', this, this.date);
23330         
23331     },
23332     
23333     onMousedown: function(e)
23334     {
23335         e.stopPropagation();
23336         e.preventDefault();
23337     },
23338     
23339     keyup: function(e)
23340     {
23341         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23342         this.update();
23343     },
23344
23345     setValue: function(v)
23346     {
23347         if(this.fireEvent('beforeselect', this, v) !== false){
23348             var d = new Date(this.parseDate(v) ).clearTime();
23349         
23350             if(isNaN(d.getTime())){
23351                 this.date = this.viewDate = '';
23352                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23353                 return;
23354             }
23355
23356             v = this.formatDate(d);
23357
23358             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23359
23360             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23361
23362             this.update();
23363
23364             this.fireEvent('select', this, this.date);
23365         }
23366     },
23367     
23368     getValue: function()
23369     {
23370         return this.formatDate(this.date);
23371     },
23372     
23373     fireKey: function(e)
23374     {
23375         if (!this.picker().isVisible()){
23376             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23377                 this.showPopup();
23378             }
23379             return;
23380         }
23381         
23382         var dateChanged = false,
23383         dir, day, month,
23384         newDate, newViewDate;
23385         
23386         switch(e.keyCode){
23387             case 27: // escape
23388                 this.hidePopup();
23389                 e.preventDefault();
23390                 break;
23391             case 37: // left
23392             case 39: // right
23393                 if (!this.keyboardNavigation) {
23394                     break;
23395                 }
23396                 dir = e.keyCode == 37 ? -1 : 1;
23397                 
23398                 if (e.ctrlKey){
23399                     newDate = this.moveYear(this.date, dir);
23400                     newViewDate = this.moveYear(this.viewDate, dir);
23401                 } else if (e.shiftKey){
23402                     newDate = this.moveMonth(this.date, dir);
23403                     newViewDate = this.moveMonth(this.viewDate, dir);
23404                 } else {
23405                     newDate = new Date(this.date);
23406                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23407                     newViewDate = new Date(this.viewDate);
23408                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23409                 }
23410                 if (this.dateWithinRange(newDate)){
23411                     this.date = newDate;
23412                     this.viewDate = newViewDate;
23413                     this.setValue(this.formatDate(this.date));
23414 //                    this.update();
23415                     e.preventDefault();
23416                     dateChanged = true;
23417                 }
23418                 break;
23419             case 38: // up
23420             case 40: // down
23421                 if (!this.keyboardNavigation) {
23422                     break;
23423                 }
23424                 dir = e.keyCode == 38 ? -1 : 1;
23425                 if (e.ctrlKey){
23426                     newDate = this.moveYear(this.date, dir);
23427                     newViewDate = this.moveYear(this.viewDate, dir);
23428                 } else if (e.shiftKey){
23429                     newDate = this.moveMonth(this.date, dir);
23430                     newViewDate = this.moveMonth(this.viewDate, dir);
23431                 } else {
23432                     newDate = new Date(this.date);
23433                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23434                     newViewDate = new Date(this.viewDate);
23435                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23436                 }
23437                 if (this.dateWithinRange(newDate)){
23438                     this.date = newDate;
23439                     this.viewDate = newViewDate;
23440                     this.setValue(this.formatDate(this.date));
23441 //                    this.update();
23442                     e.preventDefault();
23443                     dateChanged = true;
23444                 }
23445                 break;
23446             case 13: // enter
23447                 this.setValue(this.formatDate(this.date));
23448                 this.hidePopup();
23449                 e.preventDefault();
23450                 break;
23451             case 9: // tab
23452                 this.setValue(this.formatDate(this.date));
23453                 this.hidePopup();
23454                 break;
23455             case 16: // shift
23456             case 17: // ctrl
23457             case 18: // alt
23458                 break;
23459             default :
23460                 this.hidePopup();
23461                 
23462         }
23463     },
23464     
23465     
23466     onClick: function(e) 
23467     {
23468         e.stopPropagation();
23469         e.preventDefault();
23470         
23471         var target = e.getTarget();
23472         
23473         if(target.nodeName.toLowerCase() === 'i'){
23474             target = Roo.get(target).dom.parentNode;
23475         }
23476         
23477         var nodeName = target.nodeName;
23478         var className = target.className;
23479         var html = target.innerHTML;
23480         //Roo.log(nodeName);
23481         
23482         switch(nodeName.toLowerCase()) {
23483             case 'th':
23484                 switch(className) {
23485                     case 'switch':
23486                         this.showMode(1);
23487                         break;
23488                     case 'prev':
23489                     case 'next':
23490                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23491                         switch(this.viewMode){
23492                                 case 0:
23493                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23494                                         break;
23495                                 case 1:
23496                                 case 2:
23497                                         this.viewDate = this.moveYear(this.viewDate, dir);
23498                                         break;
23499                         }
23500                         this.fill();
23501                         break;
23502                     case 'today':
23503                         var date = new Date();
23504                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23505 //                        this.fill()
23506                         this.setValue(this.formatDate(this.date));
23507                         
23508                         this.hidePopup();
23509                         break;
23510                 }
23511                 break;
23512             case 'span':
23513                 if (className.indexOf('disabled') < 0) {
23514                 if (!this.viewDate) {
23515                     this.viewDate = new Date();
23516                 }
23517                 this.viewDate.setUTCDate(1);
23518                     if (className.indexOf('month') > -1) {
23519                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23520                     } else {
23521                         var year = parseInt(html, 10) || 0;
23522                         this.viewDate.setUTCFullYear(year);
23523                         
23524                     }
23525                     
23526                     if(this.singleMode){
23527                         this.setValue(this.formatDate(this.viewDate));
23528                         this.hidePopup();
23529                         return;
23530                     }
23531                     
23532                     this.showMode(-1);
23533                     this.fill();
23534                 }
23535                 break;
23536                 
23537             case 'td':
23538                 //Roo.log(className);
23539                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23540                     var day = parseInt(html, 10) || 1;
23541                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23542                         month = (this.viewDate || new Date()).getUTCMonth();
23543
23544                     if (className.indexOf('old') > -1) {
23545                         if(month === 0 ){
23546                             month = 11;
23547                             year -= 1;
23548                         }else{
23549                             month -= 1;
23550                         }
23551                     } else if (className.indexOf('new') > -1) {
23552                         if (month == 11) {
23553                             month = 0;
23554                             year += 1;
23555                         } else {
23556                             month += 1;
23557                         }
23558                     }
23559                     //Roo.log([year,month,day]);
23560                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23561                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23562 //                    this.fill();
23563                     //Roo.log(this.formatDate(this.date));
23564                     this.setValue(this.formatDate(this.date));
23565                     this.hidePopup();
23566                 }
23567                 break;
23568         }
23569     },
23570     
23571     setStartDate: function(startDate)
23572     {
23573         this.startDate = startDate || -Infinity;
23574         if (this.startDate !== -Infinity) {
23575             this.startDate = this.parseDate(this.startDate);
23576         }
23577         this.update();
23578         this.updateNavArrows();
23579     },
23580
23581     setEndDate: function(endDate)
23582     {
23583         this.endDate = endDate || Infinity;
23584         if (this.endDate !== Infinity) {
23585             this.endDate = this.parseDate(this.endDate);
23586         }
23587         this.update();
23588         this.updateNavArrows();
23589     },
23590     
23591     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23592     {
23593         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23594         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23595             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23596         }
23597         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23598             return parseInt(d, 10);
23599         });
23600         this.update();
23601         this.updateNavArrows();
23602     },
23603     
23604     updateNavArrows: function() 
23605     {
23606         if(this.singleMode){
23607             return;
23608         }
23609         
23610         var d = new Date(this.viewDate),
23611         year = d.getUTCFullYear(),
23612         month = d.getUTCMonth();
23613         
23614         Roo.each(this.picker().select('.prev', true).elements, function(v){
23615             v.show();
23616             switch (this.viewMode) {
23617                 case 0:
23618
23619                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23620                         v.hide();
23621                     }
23622                     break;
23623                 case 1:
23624                 case 2:
23625                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23626                         v.hide();
23627                     }
23628                     break;
23629             }
23630         });
23631         
23632         Roo.each(this.picker().select('.next', true).elements, function(v){
23633             v.show();
23634             switch (this.viewMode) {
23635                 case 0:
23636
23637                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23638                         v.hide();
23639                     }
23640                     break;
23641                 case 1:
23642                 case 2:
23643                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23644                         v.hide();
23645                     }
23646                     break;
23647             }
23648         })
23649     },
23650     
23651     moveMonth: function(date, dir)
23652     {
23653         if (!dir) {
23654             return date;
23655         }
23656         var new_date = new Date(date.valueOf()),
23657         day = new_date.getUTCDate(),
23658         month = new_date.getUTCMonth(),
23659         mag = Math.abs(dir),
23660         new_month, test;
23661         dir = dir > 0 ? 1 : -1;
23662         if (mag == 1){
23663             test = dir == -1
23664             // If going back one month, make sure month is not current month
23665             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23666             ? function(){
23667                 return new_date.getUTCMonth() == month;
23668             }
23669             // If going forward one month, make sure month is as expected
23670             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23671             : function(){
23672                 return new_date.getUTCMonth() != new_month;
23673             };
23674             new_month = month + dir;
23675             new_date.setUTCMonth(new_month);
23676             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23677             if (new_month < 0 || new_month > 11) {
23678                 new_month = (new_month + 12) % 12;
23679             }
23680         } else {
23681             // For magnitudes >1, move one month at a time...
23682             for (var i=0; i<mag; i++) {
23683                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23684                 new_date = this.moveMonth(new_date, dir);
23685             }
23686             // ...then reset the day, keeping it in the new month
23687             new_month = new_date.getUTCMonth();
23688             new_date.setUTCDate(day);
23689             test = function(){
23690                 return new_month != new_date.getUTCMonth();
23691             };
23692         }
23693         // Common date-resetting loop -- if date is beyond end of month, make it
23694         // end of month
23695         while (test()){
23696             new_date.setUTCDate(--day);
23697             new_date.setUTCMonth(new_month);
23698         }
23699         return new_date;
23700     },
23701
23702     moveYear: function(date, dir)
23703     {
23704         return this.moveMonth(date, dir*12);
23705     },
23706
23707     dateWithinRange: function(date)
23708     {
23709         return date >= this.startDate && date <= this.endDate;
23710     },
23711
23712     
23713     remove: function() 
23714     {
23715         this.picker().remove();
23716     },
23717     
23718     validateValue : function(value)
23719     {
23720         if(this.getVisibilityEl().hasClass('hidden')){
23721             return true;
23722         }
23723         
23724         if(value.length < 1)  {
23725             if(this.allowBlank){
23726                 return true;
23727             }
23728             return false;
23729         }
23730         
23731         if(value.length < this.minLength){
23732             return false;
23733         }
23734         if(value.length > this.maxLength){
23735             return false;
23736         }
23737         if(this.vtype){
23738             var vt = Roo.form.VTypes;
23739             if(!vt[this.vtype](value, this)){
23740                 return false;
23741             }
23742         }
23743         if(typeof this.validator == "function"){
23744             var msg = this.validator(value);
23745             if(msg !== true){
23746                 return false;
23747             }
23748         }
23749         
23750         if(this.regex && !this.regex.test(value)){
23751             return false;
23752         }
23753         
23754         if(typeof(this.parseDate(value)) == 'undefined'){
23755             return false;
23756         }
23757         
23758         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23759             return false;
23760         }      
23761         
23762         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23763             return false;
23764         } 
23765         
23766         
23767         return true;
23768     },
23769     
23770     reset : function()
23771     {
23772         this.date = this.viewDate = '';
23773         
23774         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23775     }
23776    
23777 });
23778
23779 Roo.apply(Roo.bootstrap.form.DateField,  {
23780     
23781     head : {
23782         tag: 'thead',
23783         cn: [
23784         {
23785             tag: 'tr',
23786             cn: [
23787             {
23788                 tag: 'th',
23789                 cls: 'prev',
23790                 html: '<i class="fa fa-arrow-left"/>'
23791             },
23792             {
23793                 tag: 'th',
23794                 cls: 'switch',
23795                 colspan: '5'
23796             },
23797             {
23798                 tag: 'th',
23799                 cls: 'next',
23800                 html: '<i class="fa fa-arrow-right"/>'
23801             }
23802
23803             ]
23804         }
23805         ]
23806     },
23807     
23808     content : {
23809         tag: 'tbody',
23810         cn: [
23811         {
23812             tag: 'tr',
23813             cn: [
23814             {
23815                 tag: 'td',
23816                 colspan: '7'
23817             }
23818             ]
23819         }
23820         ]
23821     },
23822     
23823     footer : {
23824         tag: 'tfoot',
23825         cn: [
23826         {
23827             tag: 'tr',
23828             cn: [
23829             {
23830                 tag: 'th',
23831                 colspan: '7',
23832                 cls: 'today'
23833             }
23834                     
23835             ]
23836         }
23837         ]
23838     },
23839     
23840     dates:{
23841         en: {
23842             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23843             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23844             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23845             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23846             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23847             today: "Today"
23848         }
23849     },
23850     
23851     modes: [
23852     {
23853         clsName: 'days',
23854         navFnc: 'Month',
23855         navStep: 1
23856     },
23857     {
23858         clsName: 'months',
23859         navFnc: 'FullYear',
23860         navStep: 1
23861     },
23862     {
23863         clsName: 'years',
23864         navFnc: 'FullYear',
23865         navStep: 10
23866     }]
23867 });
23868
23869 Roo.apply(Roo.bootstrap.form.DateField,  {
23870   
23871     template : {
23872         tag: 'div',
23873         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23874         cn: [
23875         {
23876             tag: 'div',
23877             cls: 'datepicker-days',
23878             cn: [
23879             {
23880                 tag: 'table',
23881                 cls: 'table-condensed',
23882                 cn:[
23883                 Roo.bootstrap.form.DateField.head,
23884                 {
23885                     tag: 'tbody'
23886                 },
23887                 Roo.bootstrap.form.DateField.footer
23888                 ]
23889             }
23890             ]
23891         },
23892         {
23893             tag: 'div',
23894             cls: 'datepicker-months',
23895             cn: [
23896             {
23897                 tag: 'table',
23898                 cls: 'table-condensed',
23899                 cn:[
23900                 Roo.bootstrap.form.DateField.head,
23901                 Roo.bootstrap.form.DateField.content,
23902                 Roo.bootstrap.form.DateField.footer
23903                 ]
23904             }
23905             ]
23906         },
23907         {
23908             tag: 'div',
23909             cls: 'datepicker-years',
23910             cn: [
23911             {
23912                 tag: 'table',
23913                 cls: 'table-condensed',
23914                 cn:[
23915                 Roo.bootstrap.form.DateField.head,
23916                 Roo.bootstrap.form.DateField.content,
23917                 Roo.bootstrap.form.DateField.footer
23918                 ]
23919             }
23920             ]
23921         }
23922         ]
23923     }
23924 });
23925
23926  
23927
23928  /*
23929  * - LGPL
23930  *
23931  * TimeField
23932  * 
23933  */
23934
23935 /**
23936  * @class Roo.bootstrap.form.TimeField
23937  * @extends Roo.bootstrap.form.Input
23938  * Bootstrap DateField class
23939  * 
23940  * 
23941  * @constructor
23942  * Create a new TimeField
23943  * @param {Object} config The config object
23944  */
23945
23946 Roo.bootstrap.form.TimeField = function(config){
23947     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23948     this.addEvents({
23949             /**
23950              * @event show
23951              * Fires when this field show.
23952              * @param {Roo.bootstrap.form.DateField} thisthis
23953              * @param {Mixed} date The date value
23954              */
23955             show : true,
23956             /**
23957              * @event show
23958              * Fires when this field hide.
23959              * @param {Roo.bootstrap.form.DateField} this
23960              * @param {Mixed} date The date value
23961              */
23962             hide : true,
23963             /**
23964              * @event select
23965              * Fires when select a date.
23966              * @param {Roo.bootstrap.form.DateField} this
23967              * @param {Mixed} date The date value
23968              */
23969             select : true
23970         });
23971 };
23972
23973 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23974     
23975     /**
23976      * @cfg {String} format
23977      * The default time format string which can be overriden for localization support.  The format must be
23978      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23979      */
23980     format : "H:i",
23981
23982     getAutoCreate : function()
23983     {
23984         this.after = '<i class="fa far fa-clock"></i>';
23985         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23986         
23987          
23988     },
23989     onRender: function(ct, position)
23990     {
23991         
23992         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23993                 
23994         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23995         
23996         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23997         
23998         this.pop = this.picker().select('>.datepicker-time',true).first();
23999         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24000         
24001         this.picker().on('mousedown', this.onMousedown, this);
24002         this.picker().on('click', this.onClick, this);
24003         
24004         this.picker().addClass('datepicker-dropdown');
24005     
24006         this.fillTime();
24007         this.update();
24008             
24009         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24010         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24011         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24012         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24013         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24014         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24015
24016     },
24017     
24018     fireKey: function(e){
24019         if (!this.picker().isVisible()){
24020             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24021                 this.show();
24022             }
24023             return;
24024         }
24025
24026         e.preventDefault();
24027         
24028         switch(e.keyCode){
24029             case 27: // escape
24030                 this.hide();
24031                 break;
24032             case 37: // left
24033             case 39: // right
24034                 this.onTogglePeriod();
24035                 break;
24036             case 38: // up
24037                 this.onIncrementMinutes();
24038                 break;
24039             case 40: // down
24040                 this.onDecrementMinutes();
24041                 break;
24042             case 13: // enter
24043             case 9: // tab
24044                 this.setTime();
24045                 break;
24046         }
24047     },
24048     
24049     onClick: function(e) {
24050         e.stopPropagation();
24051         e.preventDefault();
24052     },
24053     
24054     picker : function()
24055     {
24056         return this.pickerEl;
24057     },
24058     
24059     fillTime: function()
24060     {    
24061         var time = this.pop.select('tbody', true).first();
24062         
24063         time.dom.innerHTML = '';
24064         
24065         time.createChild({
24066             tag: 'tr',
24067             cn: [
24068                 {
24069                     tag: 'td',
24070                     cn: [
24071                         {
24072                             tag: 'a',
24073                             href: '#',
24074                             cls: 'btn',
24075                             cn: [
24076                                 {
24077                                     tag: 'i',
24078                                     cls: 'hours-up fa fas fa-chevron-up'
24079                                 }
24080                             ]
24081                         } 
24082                     ]
24083                 },
24084                 {
24085                     tag: 'td',
24086                     cls: 'separator'
24087                 },
24088                 {
24089                     tag: 'td',
24090                     cn: [
24091                         {
24092                             tag: 'a',
24093                             href: '#',
24094                             cls: 'btn',
24095                             cn: [
24096                                 {
24097                                     tag: 'i',
24098                                     cls: 'minutes-up fa fas fa-chevron-up'
24099                                 }
24100                             ]
24101                         }
24102                     ]
24103                 },
24104                 {
24105                     tag: 'td',
24106                     cls: 'separator'
24107                 }
24108             ]
24109         });
24110         
24111         time.createChild({
24112             tag: 'tr',
24113             cn: [
24114                 {
24115                     tag: 'td',
24116                     cn: [
24117                         {
24118                             tag: 'span',
24119                             cls: 'timepicker-hour',
24120                             html: '00'
24121                         }  
24122                     ]
24123                 },
24124                 {
24125                     tag: 'td',
24126                     cls: 'separator',
24127                     html: ':'
24128                 },
24129                 {
24130                     tag: 'td',
24131                     cn: [
24132                         {
24133                             tag: 'span',
24134                             cls: 'timepicker-minute',
24135                             html: '00'
24136                         }  
24137                     ]
24138                 },
24139                 {
24140                     tag: 'td',
24141                     cls: 'separator'
24142                 },
24143                 {
24144                     tag: 'td',
24145                     cn: [
24146                         {
24147                             tag: 'button',
24148                             type: 'button',
24149                             cls: 'btn btn-primary period',
24150                             html: 'AM'
24151                             
24152                         }
24153                     ]
24154                 }
24155             ]
24156         });
24157         
24158         time.createChild({
24159             tag: 'tr',
24160             cn: [
24161                 {
24162                     tag: 'td',
24163                     cn: [
24164                         {
24165                             tag: 'a',
24166                             href: '#',
24167                             cls: 'btn',
24168                             cn: [
24169                                 {
24170                                     tag: 'span',
24171                                     cls: 'hours-down fa fas fa-chevron-down'
24172                                 }
24173                             ]
24174                         }
24175                     ]
24176                 },
24177                 {
24178                     tag: 'td',
24179                     cls: 'separator'
24180                 },
24181                 {
24182                     tag: 'td',
24183                     cn: [
24184                         {
24185                             tag: 'a',
24186                             href: '#',
24187                             cls: 'btn',
24188                             cn: [
24189                                 {
24190                                     tag: 'span',
24191                                     cls: 'minutes-down fa fas fa-chevron-down'
24192                                 }
24193                             ]
24194                         }
24195                     ]
24196                 },
24197                 {
24198                     tag: 'td',
24199                     cls: 'separator'
24200                 }
24201             ]
24202         });
24203         
24204     },
24205     
24206     update: function()
24207     {
24208         
24209         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24210         
24211         this.fill();
24212     },
24213     
24214     fill: function() 
24215     {
24216         var hours = this.time.getHours();
24217         var minutes = this.time.getMinutes();
24218         var period = 'AM';
24219         
24220         if(hours > 11){
24221             period = 'PM';
24222         }
24223         
24224         if(hours == 0){
24225             hours = 12;
24226         }
24227         
24228         
24229         if(hours > 12){
24230             hours = hours - 12;
24231         }
24232         
24233         if(hours < 10){
24234             hours = '0' + hours;
24235         }
24236         
24237         if(minutes < 10){
24238             minutes = '0' + minutes;
24239         }
24240         
24241         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24242         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24243         this.pop.select('button', true).first().dom.innerHTML = period;
24244         
24245     },
24246     
24247     place: function()
24248     {   
24249         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24250         
24251         var cls = ['bottom'];
24252         
24253         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24254             cls.pop();
24255             cls.push('top');
24256         }
24257         
24258         cls.push('right');
24259         
24260         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24261             cls.pop();
24262             cls.push('left');
24263         }
24264         //this.picker().setXY(20000,20000);
24265         this.picker().addClass(cls.join('-'));
24266         
24267         var _this = this;
24268         
24269         Roo.each(cls, function(c){
24270             if(c == 'bottom'){
24271                 (function() {
24272                  //  
24273                 }).defer(200);
24274                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24275                 //_this.picker().setTop(_this.inputEl().getHeight());
24276                 return;
24277             }
24278             if(c == 'top'){
24279                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24280                 
24281                 //_this.picker().setTop(0 - _this.picker().getHeight());
24282                 return;
24283             }
24284             /*
24285             if(c == 'left'){
24286                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24287                 return;
24288             }
24289             if(c == 'right'){
24290                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24291                 return;
24292             }
24293             */
24294         });
24295         
24296     },
24297   
24298     onFocus : function()
24299     {
24300         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24301         this.show();
24302     },
24303     
24304     onBlur : function()
24305     {
24306         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24307         this.hide();
24308     },
24309     
24310     show : function()
24311     {
24312         this.picker().show();
24313         this.pop.show();
24314         this.update();
24315         this.place();
24316         
24317         this.fireEvent('show', this, this.date);
24318     },
24319     
24320     hide : function()
24321     {
24322         this.picker().hide();
24323         this.pop.hide();
24324         
24325         this.fireEvent('hide', this, this.date);
24326     },
24327     
24328     setTime : function()
24329     {
24330         this.hide();
24331         this.setValue(this.time.format(this.format));
24332         
24333         this.fireEvent('select', this, this.date);
24334         
24335         
24336     },
24337     
24338     onMousedown: function(e){
24339         e.stopPropagation();
24340         e.preventDefault();
24341     },
24342     
24343     onIncrementHours: function()
24344     {
24345         Roo.log('onIncrementHours');
24346         this.time = this.time.add(Date.HOUR, 1);
24347         this.update();
24348         
24349     },
24350     
24351     onDecrementHours: function()
24352     {
24353         Roo.log('onDecrementHours');
24354         this.time = this.time.add(Date.HOUR, -1);
24355         this.update();
24356     },
24357     
24358     onIncrementMinutes: function()
24359     {
24360         Roo.log('onIncrementMinutes');
24361         this.time = this.time.add(Date.MINUTE, 1);
24362         this.update();
24363     },
24364     
24365     onDecrementMinutes: function()
24366     {
24367         Roo.log('onDecrementMinutes');
24368         this.time = this.time.add(Date.MINUTE, -1);
24369         this.update();
24370     },
24371     
24372     onTogglePeriod: function()
24373     {
24374         Roo.log('onTogglePeriod');
24375         this.time = this.time.add(Date.HOUR, 12);
24376         this.update();
24377     }
24378     
24379    
24380 });
24381  
24382
24383 Roo.apply(Roo.bootstrap.form.TimeField,  {
24384   
24385     template : {
24386         tag: 'div',
24387         cls: 'datepicker dropdown-menu',
24388         cn: [
24389             {
24390                 tag: 'div',
24391                 cls: 'datepicker-time',
24392                 cn: [
24393                 {
24394                     tag: 'table',
24395                     cls: 'table-condensed',
24396                     cn:[
24397                         {
24398                             tag: 'tbody',
24399                             cn: [
24400                                 {
24401                                     tag: 'tr',
24402                                     cn: [
24403                                     {
24404                                         tag: 'td',
24405                                         colspan: '7'
24406                                     }
24407                                     ]
24408                                 }
24409                             ]
24410                         },
24411                         {
24412                             tag: 'tfoot',
24413                             cn: [
24414                                 {
24415                                     tag: 'tr',
24416                                     cn: [
24417                                     {
24418                                         tag: 'th',
24419                                         colspan: '7',
24420                                         cls: '',
24421                                         cn: [
24422                                             {
24423                                                 tag: 'button',
24424                                                 cls: 'btn btn-info ok',
24425                                                 html: 'OK'
24426                                             }
24427                                         ]
24428                                     }
24429                     
24430                                     ]
24431                                 }
24432                             ]
24433                         }
24434                     ]
24435                 }
24436                 ]
24437             }
24438         ]
24439     }
24440 });
24441
24442  
24443
24444  /*
24445  * - LGPL
24446  *
24447  * MonthField
24448  * 
24449  */
24450
24451 /**
24452  * @class Roo.bootstrap.form.MonthField
24453  * @extends Roo.bootstrap.form.Input
24454  * Bootstrap MonthField class
24455  * 
24456  * @cfg {String} language default en
24457  * 
24458  * @constructor
24459  * Create a new MonthField
24460  * @param {Object} config The config object
24461  */
24462
24463 Roo.bootstrap.form.MonthField = function(config){
24464     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24465     
24466     this.addEvents({
24467         /**
24468          * @event show
24469          * Fires when this field show.
24470          * @param {Roo.bootstrap.form.MonthField} this
24471          * @param {Mixed} date The date value
24472          */
24473         show : true,
24474         /**
24475          * @event show
24476          * Fires when this field hide.
24477          * @param {Roo.bootstrap.form.MonthField} this
24478          * @param {Mixed} date The date value
24479          */
24480         hide : true,
24481         /**
24482          * @event select
24483          * Fires when select a date.
24484          * @param {Roo.bootstrap.form.MonthField} this
24485          * @param {String} oldvalue The old value
24486          * @param {String} newvalue The new value
24487          */
24488         select : true
24489     });
24490 };
24491
24492 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24493     
24494     onRender: function(ct, position)
24495     {
24496         
24497         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24498         
24499         this.language = this.language || 'en';
24500         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24501         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24502         
24503         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24504         this.isInline = false;
24505         this.isInput = true;
24506         this.component = this.el.select('.add-on', true).first() || false;
24507         this.component = (this.component && this.component.length === 0) ? false : this.component;
24508         this.hasInput = this.component && this.inputEL().length;
24509         
24510         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24511         
24512         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24513         
24514         this.picker().on('mousedown', this.onMousedown, this);
24515         this.picker().on('click', this.onClick, this);
24516         
24517         this.picker().addClass('datepicker-dropdown');
24518         
24519         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24520             v.setStyle('width', '189px');
24521         });
24522         
24523         this.fillMonths();
24524         
24525         this.update();
24526         
24527         if(this.isInline) {
24528             this.show();
24529         }
24530         
24531     },
24532     
24533     setValue: function(v, suppressEvent)
24534     {   
24535         var o = this.getValue();
24536         
24537         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24538         
24539         this.update();
24540
24541         if(suppressEvent !== true){
24542             this.fireEvent('select', this, o, v);
24543         }
24544         
24545     },
24546     
24547     getValue: function()
24548     {
24549         return this.value;
24550     },
24551     
24552     onClick: function(e) 
24553     {
24554         e.stopPropagation();
24555         e.preventDefault();
24556         
24557         var target = e.getTarget();
24558         
24559         if(target.nodeName.toLowerCase() === 'i'){
24560             target = Roo.get(target).dom.parentNode;
24561         }
24562         
24563         var nodeName = target.nodeName;
24564         var className = target.className;
24565         var html = target.innerHTML;
24566         
24567         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24568             return;
24569         }
24570         
24571         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24572         
24573         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24574         
24575         this.hide();
24576                         
24577     },
24578     
24579     picker : function()
24580     {
24581         return this.pickerEl;
24582     },
24583     
24584     fillMonths: function()
24585     {    
24586         var i = 0;
24587         var months = this.picker().select('>.datepicker-months td', true).first();
24588         
24589         months.dom.innerHTML = '';
24590         
24591         while (i < 12) {
24592             var month = {
24593                 tag: 'span',
24594                 cls: 'month',
24595                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24596             };
24597             
24598             months.createChild(month);
24599         }
24600         
24601     },
24602     
24603     update: function()
24604     {
24605         var _this = this;
24606         
24607         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24608             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24609         }
24610         
24611         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24612             e.removeClass('active');
24613             
24614             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24615                 e.addClass('active');
24616             }
24617         })
24618     },
24619     
24620     place: function()
24621     {
24622         if(this.isInline) {
24623             return;
24624         }
24625         
24626         this.picker().removeClass(['bottom', 'top']);
24627         
24628         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24629             /*
24630              * place to the top of element!
24631              *
24632              */
24633             
24634             this.picker().addClass('top');
24635             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24636             
24637             return;
24638         }
24639         
24640         this.picker().addClass('bottom');
24641         
24642         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24643     },
24644     
24645     onFocus : function()
24646     {
24647         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24648         this.show();
24649     },
24650     
24651     onBlur : function()
24652     {
24653         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24654         
24655         var d = this.inputEl().getValue();
24656         
24657         this.setValue(d);
24658                 
24659         this.hide();
24660     },
24661     
24662     show : function()
24663     {
24664         this.picker().show();
24665         this.picker().select('>.datepicker-months', true).first().show();
24666         this.update();
24667         this.place();
24668         
24669         this.fireEvent('show', this, this.date);
24670     },
24671     
24672     hide : function()
24673     {
24674         if(this.isInline) {
24675             return;
24676         }
24677         this.picker().hide();
24678         this.fireEvent('hide', this, this.date);
24679         
24680     },
24681     
24682     onMousedown: function(e)
24683     {
24684         e.stopPropagation();
24685         e.preventDefault();
24686     },
24687     
24688     keyup: function(e)
24689     {
24690         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24691         this.update();
24692     },
24693
24694     fireKey: function(e)
24695     {
24696         if (!this.picker().isVisible()){
24697             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24698                 this.show();
24699             }
24700             return;
24701         }
24702         
24703         var dir;
24704         
24705         switch(e.keyCode){
24706             case 27: // escape
24707                 this.hide();
24708                 e.preventDefault();
24709                 break;
24710             case 37: // left
24711             case 39: // right
24712                 dir = e.keyCode == 37 ? -1 : 1;
24713                 
24714                 this.vIndex = this.vIndex + dir;
24715                 
24716                 if(this.vIndex < 0){
24717                     this.vIndex = 0;
24718                 }
24719                 
24720                 if(this.vIndex > 11){
24721                     this.vIndex = 11;
24722                 }
24723                 
24724                 if(isNaN(this.vIndex)){
24725                     this.vIndex = 0;
24726                 }
24727                 
24728                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24729                 
24730                 break;
24731             case 38: // up
24732             case 40: // down
24733                 
24734                 dir = e.keyCode == 38 ? -1 : 1;
24735                 
24736                 this.vIndex = this.vIndex + dir * 4;
24737                 
24738                 if(this.vIndex < 0){
24739                     this.vIndex = 0;
24740                 }
24741                 
24742                 if(this.vIndex > 11){
24743                     this.vIndex = 11;
24744                 }
24745                 
24746                 if(isNaN(this.vIndex)){
24747                     this.vIndex = 0;
24748                 }
24749                 
24750                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24751                 break;
24752                 
24753             case 13: // enter
24754                 
24755                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24756                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24757                 }
24758                 
24759                 this.hide();
24760                 e.preventDefault();
24761                 break;
24762             case 9: // tab
24763                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24764                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24765                 }
24766                 this.hide();
24767                 break;
24768             case 16: // shift
24769             case 17: // ctrl
24770             case 18: // alt
24771                 break;
24772             default :
24773                 this.hide();
24774                 
24775         }
24776     },
24777     
24778     remove: function() 
24779     {
24780         this.picker().remove();
24781     }
24782    
24783 });
24784
24785 Roo.apply(Roo.bootstrap.form.MonthField,  {
24786     
24787     content : {
24788         tag: 'tbody',
24789         cn: [
24790         {
24791             tag: 'tr',
24792             cn: [
24793             {
24794                 tag: 'td',
24795                 colspan: '7'
24796             }
24797             ]
24798         }
24799         ]
24800     },
24801     
24802     dates:{
24803         en: {
24804             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24805             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24806         }
24807     }
24808 });
24809
24810 Roo.apply(Roo.bootstrap.form.MonthField,  {
24811   
24812     template : {
24813         tag: 'div',
24814         cls: 'datepicker dropdown-menu roo-dynamic',
24815         cn: [
24816             {
24817                 tag: 'div',
24818                 cls: 'datepicker-months',
24819                 cn: [
24820                 {
24821                     tag: 'table',
24822                     cls: 'table-condensed',
24823                     cn:[
24824                         Roo.bootstrap.form.DateField.content
24825                     ]
24826                 }
24827                 ]
24828             }
24829         ]
24830     }
24831 });
24832
24833  
24834
24835  
24836  /*
24837  * - LGPL
24838  *
24839  * CheckBox
24840  * 
24841  */
24842
24843 /**
24844  * @class Roo.bootstrap.form.CheckBox
24845  * @extends Roo.bootstrap.form.Input
24846  * Bootstrap CheckBox class
24847  * 
24848  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24849  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24850  * @cfg {String} boxLabel The text that appears beside the checkbox
24851  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24852  * @cfg {Boolean} checked initnal the element
24853  * @cfg {Boolean} inline inline the element (default false)
24854  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24855  * @cfg {String} tooltip label tooltip
24856  * 
24857  * @constructor
24858  * Create a new CheckBox
24859  * @param {Object} config The config object
24860  */
24861
24862 Roo.bootstrap.form.CheckBox = function(config){
24863     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24864    
24865     this.addEvents({
24866         /**
24867         * @event check
24868         * Fires when the element is checked or unchecked.
24869         * @param {Roo.bootstrap.form.CheckBox} this This input
24870         * @param {Boolean} checked The new checked value
24871         */
24872        check : true,
24873        /**
24874         * @event click
24875         * Fires when the element is click.
24876         * @param {Roo.bootstrap.form.CheckBox} this This input
24877         */
24878        click : true
24879     });
24880     
24881 };
24882
24883 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24884   
24885     inputType: 'checkbox',
24886     inputValue: 1,
24887     valueOff: 0,
24888     boxLabel: false,
24889     checked: false,
24890     weight : false,
24891     inline: false,
24892     tooltip : '',
24893     
24894     // checkbox success does not make any sense really.. 
24895     invalidClass : "",
24896     validClass : "",
24897     
24898     
24899     getAutoCreate : function()
24900     {
24901         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24902         
24903         var id = Roo.id();
24904         
24905         var cfg = {};
24906         
24907         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24908         
24909         if(this.inline){
24910             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24911         }
24912         
24913         var input =  {
24914             tag: 'input',
24915             id : id,
24916             type : this.inputType,
24917             value : this.inputValue,
24918             cls : 'roo-' + this.inputType, //'form-box',
24919             placeholder : this.placeholder || ''
24920             
24921         };
24922         
24923         if(this.inputType != 'radio'){
24924             var hidden =  {
24925                 tag: 'input',
24926                 type : 'hidden',
24927                 cls : 'roo-hidden-value',
24928                 value : this.checked ? this.inputValue : this.valueOff
24929             };
24930         }
24931         
24932             
24933         if (this.weight) { // Validity check?
24934             cfg.cls += " " + this.inputType + "-" + this.weight;
24935         }
24936         
24937         if (this.disabled) {
24938             input.disabled=true;
24939         }
24940         
24941         if(this.checked){
24942             input.checked = this.checked;
24943         }
24944         
24945         if (this.name) {
24946             
24947             input.name = this.name;
24948             
24949             if(this.inputType != 'radio'){
24950                 hidden.name = this.name;
24951                 input.name = '_hidden_' + this.name;
24952             }
24953         }
24954         
24955         if (this.size) {
24956             input.cls += ' input-' + this.size;
24957         }
24958         
24959         var settings=this;
24960         
24961         ['xs','sm','md','lg'].map(function(size){
24962             if (settings[size]) {
24963                 cfg.cls += ' col-' + size + '-' + settings[size];
24964             }
24965         });
24966         
24967         var inputblock = input;
24968          
24969         if (this.before || this.after) {
24970             
24971             inputblock = {
24972                 cls : 'input-group',
24973                 cn :  [] 
24974             };
24975             
24976             if (this.before) {
24977                 inputblock.cn.push({
24978                     tag :'span',
24979                     cls : 'input-group-addon',
24980                     html : this.before
24981                 });
24982             }
24983             
24984             inputblock.cn.push(input);
24985             
24986             if(this.inputType != 'radio'){
24987                 inputblock.cn.push(hidden);
24988             }
24989             
24990             if (this.after) {
24991                 inputblock.cn.push({
24992                     tag :'span',
24993                     cls : 'input-group-addon',
24994                     html : this.after
24995                 });
24996             }
24997             
24998         }
24999         var boxLabelCfg = false;
25000         
25001         if(this.boxLabel){
25002            
25003             boxLabelCfg = {
25004                 tag: 'label',
25005                 //'for': id, // box label is handled by onclick - so no for...
25006                 cls: 'box-label',
25007                 html: this.boxLabel
25008             };
25009             if(this.tooltip){
25010                 boxLabelCfg.tooltip = this.tooltip;
25011             }
25012              
25013         }
25014         
25015         
25016         if (align ==='left' && this.fieldLabel.length) {
25017 //                Roo.log("left and has label");
25018             cfg.cn = [
25019                 {
25020                     tag: 'label',
25021                     'for' :  id,
25022                     cls : 'control-label',
25023                     html : this.fieldLabel
25024                 },
25025                 {
25026                     cls : "", 
25027                     cn: [
25028                         inputblock
25029                     ]
25030                 }
25031             ];
25032             
25033             if (boxLabelCfg) {
25034                 cfg.cn[1].cn.push(boxLabelCfg);
25035             }
25036             
25037             if(this.labelWidth > 12){
25038                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25039             }
25040             
25041             if(this.labelWidth < 13 && this.labelmd == 0){
25042                 this.labelmd = this.labelWidth;
25043             }
25044             
25045             if(this.labellg > 0){
25046                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25047                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25048             }
25049             
25050             if(this.labelmd > 0){
25051                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25052                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25053             }
25054             
25055             if(this.labelsm > 0){
25056                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25057                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25058             }
25059             
25060             if(this.labelxs > 0){
25061                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25062                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25063             }
25064             
25065         } else if ( this.fieldLabel.length) {
25066 //                Roo.log(" label");
25067                 cfg.cn = [
25068                    
25069                     {
25070                         tag: this.boxLabel ? 'span' : 'label',
25071                         'for': id,
25072                         cls: 'control-label box-input-label',
25073                         //cls : 'input-group-addon',
25074                         html : this.fieldLabel
25075                     },
25076                     
25077                     inputblock
25078                     
25079                 ];
25080                 if (boxLabelCfg) {
25081                     cfg.cn.push(boxLabelCfg);
25082                 }
25083
25084         } else {
25085             
25086 //                Roo.log(" no label && no align");
25087                 cfg.cn = [  inputblock ] ;
25088                 if (boxLabelCfg) {
25089                     cfg.cn.push(boxLabelCfg);
25090                 }
25091
25092                 
25093         }
25094         
25095        
25096         
25097         if(this.inputType != 'radio'){
25098             cfg.cn.push(hidden);
25099         }
25100         
25101         return cfg;
25102         
25103     },
25104     
25105     /**
25106      * return the real input element.
25107      */
25108     inputEl: function ()
25109     {
25110         return this.el.select('input.roo-' + this.inputType,true).first();
25111     },
25112     hiddenEl: function ()
25113     {
25114         return this.el.select('input.roo-hidden-value',true).first();
25115     },
25116     
25117     labelEl: function()
25118     {
25119         return this.el.select('label.control-label',true).first();
25120     },
25121     /* depricated... */
25122     
25123     label: function()
25124     {
25125         return this.labelEl();
25126     },
25127     
25128     boxLabelEl: function()
25129     {
25130         return this.el.select('label.box-label',true).first();
25131     },
25132     
25133     initEvents : function()
25134     {
25135 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25136         
25137         this.inputEl().on('click', this.onClick,  this);
25138         
25139         if (this.boxLabel) { 
25140             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25141         }
25142         
25143         this.startValue = this.getValue();
25144         
25145         if(this.groupId){
25146             Roo.bootstrap.form.CheckBox.register(this);
25147         }
25148     },
25149     
25150     onClick : function(e)
25151     {   
25152         if(this.fireEvent('click', this, e) !== false){
25153             this.setChecked(!this.checked);
25154         }
25155         
25156     },
25157     
25158     setChecked : function(state,suppressEvent)
25159     {
25160         this.startValue = this.getValue();
25161
25162         if(this.inputType == 'radio'){
25163             
25164             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25165                 e.dom.checked = false;
25166             });
25167             
25168             this.inputEl().dom.checked = true;
25169             
25170             this.inputEl().dom.value = this.inputValue;
25171             
25172             if(suppressEvent !== true){
25173                 this.fireEvent('check', this, true);
25174             }
25175             
25176             this.validate();
25177             
25178             return;
25179         }
25180         
25181         this.checked = state;
25182         
25183         this.inputEl().dom.checked = state;
25184         
25185         
25186         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25187         
25188         if(suppressEvent !== true){
25189             this.fireEvent('check', this, state);
25190         }
25191         
25192         this.validate();
25193     },
25194     
25195     getValue : function()
25196     {
25197         if(this.inputType == 'radio'){
25198             return this.getGroupValue();
25199         }
25200         
25201         return this.hiddenEl().dom.value;
25202         
25203     },
25204     
25205     getGroupValue : function()
25206     {
25207         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25208             return '';
25209         }
25210         
25211         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25212     },
25213     
25214     setValue : function(v,suppressEvent)
25215     {
25216         if(this.inputType == 'radio'){
25217             this.setGroupValue(v, suppressEvent);
25218             return;
25219         }
25220         
25221         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25222         
25223         this.validate();
25224     },
25225     
25226     setGroupValue : function(v, suppressEvent)
25227     {
25228         this.startValue = this.getValue();
25229         
25230         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25231             e.dom.checked = false;
25232             
25233             if(e.dom.value == v){
25234                 e.dom.checked = true;
25235             }
25236         });
25237         
25238         if(suppressEvent !== true){
25239             this.fireEvent('check', this, true);
25240         }
25241
25242         this.validate();
25243         
25244         return;
25245     },
25246     
25247     validate : function()
25248     {
25249         if(this.getVisibilityEl().hasClass('hidden')){
25250             return true;
25251         }
25252         
25253         if(
25254                 this.disabled || 
25255                 (this.inputType == 'radio' && this.validateRadio()) ||
25256                 (this.inputType == 'checkbox' && this.validateCheckbox())
25257         ){
25258             this.markValid();
25259             return true;
25260         }
25261         
25262         this.markInvalid();
25263         return false;
25264     },
25265     
25266     validateRadio : function()
25267     {
25268         if(this.getVisibilityEl().hasClass('hidden')){
25269             return true;
25270         }
25271         
25272         if(this.allowBlank){
25273             return true;
25274         }
25275         
25276         var valid = false;
25277         
25278         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25279             if(!e.dom.checked){
25280                 return;
25281             }
25282             
25283             valid = true;
25284             
25285             return false;
25286         });
25287         
25288         return valid;
25289     },
25290     
25291     validateCheckbox : function()
25292     {
25293         if(!this.groupId){
25294             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25295             //return (this.getValue() == this.inputValue) ? true : false;
25296         }
25297         
25298         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25299         
25300         if(!group){
25301             return false;
25302         }
25303         
25304         var r = false;
25305         
25306         for(var i in group){
25307             if(group[i].el.isVisible(true)){
25308                 r = false;
25309                 break;
25310             }
25311             
25312             r = true;
25313         }
25314         
25315         for(var i in group){
25316             if(r){
25317                 break;
25318             }
25319             
25320             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25321         }
25322         
25323         return r;
25324     },
25325     
25326     /**
25327      * Mark this field as valid
25328      */
25329     markValid : function()
25330     {
25331         var _this = this;
25332         
25333         this.fireEvent('valid', this);
25334         
25335         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25336         
25337         if(this.groupId){
25338             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25339         }
25340         
25341         if(label){
25342             label.markValid();
25343         }
25344
25345         if(this.inputType == 'radio'){
25346             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25347                 var fg = e.findParent('.form-group', false, true);
25348                 if (Roo.bootstrap.version == 3) {
25349                     fg.removeClass([_this.invalidClass, _this.validClass]);
25350                     fg.addClass(_this.validClass);
25351                 } else {
25352                     fg.removeClass(['is-valid', 'is-invalid']);
25353                     fg.addClass('is-valid');
25354                 }
25355             });
25356             
25357             return;
25358         }
25359
25360         if(!this.groupId){
25361             var fg = this.el.findParent('.form-group', false, true);
25362             if (Roo.bootstrap.version == 3) {
25363                 fg.removeClass([this.invalidClass, this.validClass]);
25364                 fg.addClass(this.validClass);
25365             } else {
25366                 fg.removeClass(['is-valid', 'is-invalid']);
25367                 fg.addClass('is-valid');
25368             }
25369             return;
25370         }
25371         
25372         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25373         
25374         if(!group){
25375             return;
25376         }
25377         
25378         for(var i in group){
25379             var fg = group[i].el.findParent('.form-group', false, true);
25380             if (Roo.bootstrap.version == 3) {
25381                 fg.removeClass([this.invalidClass, this.validClass]);
25382                 fg.addClass(this.validClass);
25383             } else {
25384                 fg.removeClass(['is-valid', 'is-invalid']);
25385                 fg.addClass('is-valid');
25386             }
25387         }
25388     },
25389     
25390      /**
25391      * Mark this field as invalid
25392      * @param {String} msg The validation message
25393      */
25394     markInvalid : function(msg)
25395     {
25396         if(this.allowBlank){
25397             return;
25398         }
25399         
25400         var _this = this;
25401         
25402         this.fireEvent('invalid', this, msg);
25403         
25404         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25405         
25406         if(this.groupId){
25407             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25408         }
25409         
25410         if(label){
25411             label.markInvalid();
25412         }
25413             
25414         if(this.inputType == 'radio'){
25415             
25416             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25417                 var fg = e.findParent('.form-group', false, true);
25418                 if (Roo.bootstrap.version == 3) {
25419                     fg.removeClass([_this.invalidClass, _this.validClass]);
25420                     fg.addClass(_this.invalidClass);
25421                 } else {
25422                     fg.removeClass(['is-invalid', 'is-valid']);
25423                     fg.addClass('is-invalid');
25424                 }
25425             });
25426             
25427             return;
25428         }
25429         
25430         if(!this.groupId){
25431             var fg = this.el.findParent('.form-group', false, true);
25432             if (Roo.bootstrap.version == 3) {
25433                 fg.removeClass([_this.invalidClass, _this.validClass]);
25434                 fg.addClass(_this.invalidClass);
25435             } else {
25436                 fg.removeClass(['is-invalid', 'is-valid']);
25437                 fg.addClass('is-invalid');
25438             }
25439             return;
25440         }
25441         
25442         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25443         
25444         if(!group){
25445             return;
25446         }
25447         
25448         for(var i in group){
25449             var fg = group[i].el.findParent('.form-group', false, true);
25450             if (Roo.bootstrap.version == 3) {
25451                 fg.removeClass([_this.invalidClass, _this.validClass]);
25452                 fg.addClass(_this.invalidClass);
25453             } else {
25454                 fg.removeClass(['is-invalid', 'is-valid']);
25455                 fg.addClass('is-invalid');
25456             }
25457         }
25458         
25459     },
25460     
25461     clearInvalid : function()
25462     {
25463         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25464         
25465         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25466         
25467         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25468         
25469         if (label && label.iconEl) {
25470             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25471             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25472         }
25473     },
25474     
25475     disable : function()
25476     {
25477         if(this.inputType != 'radio'){
25478             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25479             return;
25480         }
25481         
25482         var _this = this;
25483         
25484         if(this.rendered){
25485             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25486                 _this.getActionEl().addClass(this.disabledClass);
25487                 e.dom.disabled = true;
25488             });
25489         }
25490         
25491         this.disabled = true;
25492         this.fireEvent("disable", this);
25493         return this;
25494     },
25495
25496     enable : function()
25497     {
25498         if(this.inputType != 'radio'){
25499             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25500             return;
25501         }
25502         
25503         var _this = this;
25504         
25505         if(this.rendered){
25506             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25507                 _this.getActionEl().removeClass(this.disabledClass);
25508                 e.dom.disabled = false;
25509             });
25510         }
25511         
25512         this.disabled = false;
25513         this.fireEvent("enable", this);
25514         return this;
25515     },
25516     
25517     setBoxLabel : function(v)
25518     {
25519         this.boxLabel = v;
25520         
25521         if(this.rendered){
25522             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25523         }
25524     }
25525
25526 });
25527
25528 Roo.apply(Roo.bootstrap.form.CheckBox, {
25529     
25530     groups: {},
25531     
25532      /**
25533     * register a CheckBox Group
25534     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25535     */
25536     register : function(checkbox)
25537     {
25538         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25539             this.groups[checkbox.groupId] = {};
25540         }
25541         
25542         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25543             return;
25544         }
25545         
25546         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25547         
25548     },
25549     /**
25550     * fetch a CheckBox Group based on the group ID
25551     * @param {string} the group ID
25552     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25553     */
25554     get: function(groupId) {
25555         if (typeof(this.groups[groupId]) == 'undefined') {
25556             return false;
25557         }
25558         
25559         return this.groups[groupId] ;
25560     }
25561     
25562     
25563 });
25564 /*
25565  * - LGPL
25566  *
25567  * RadioItem
25568  * 
25569  */
25570
25571 /**
25572  * @class Roo.bootstrap.form.Radio
25573  * @extends Roo.bootstrap.Component
25574  * Bootstrap Radio class
25575  * @cfg {String} boxLabel - the label associated
25576  * @cfg {String} value - the value of radio
25577  * 
25578  * @constructor
25579  * Create a new Radio
25580  * @param {Object} config The config object
25581  */
25582 Roo.bootstrap.form.Radio = function(config){
25583     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25584     
25585 };
25586
25587 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25588     
25589     boxLabel : '',
25590     
25591     value : '',
25592     
25593     getAutoCreate : function()
25594     {
25595         var cfg = {
25596             tag : 'div',
25597             cls : 'form-group radio',
25598             cn : [
25599                 {
25600                     tag : 'label',
25601                     cls : 'box-label',
25602                     html : this.boxLabel
25603                 }
25604             ]
25605         };
25606         
25607         return cfg;
25608     },
25609     
25610     initEvents : function() 
25611     {
25612         this.parent().register(this);
25613         
25614         this.el.on('click', this.onClick, this);
25615         
25616     },
25617     
25618     onClick : function(e)
25619     {
25620         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25621             this.setChecked(true);
25622         }
25623     },
25624     
25625     setChecked : function(state, suppressEvent)
25626     {
25627         this.parent().setValue(this.value, suppressEvent);
25628         
25629     },
25630     
25631     setBoxLabel : function(v)
25632     {
25633         this.boxLabel = v;
25634         
25635         if(this.rendered){
25636             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25637         }
25638     }
25639     
25640 });
25641  
25642
25643  /*
25644  * - LGPL
25645  *
25646  * Input
25647  * 
25648  */
25649
25650 /**
25651  * @class Roo.bootstrap.form.SecurePass
25652  * @extends Roo.bootstrap.form.Input
25653  * Bootstrap SecurePass class
25654  *
25655  * 
25656  * @constructor
25657  * Create a new SecurePass
25658  * @param {Object} config The config object
25659  */
25660  
25661 Roo.bootstrap.form.SecurePass = function (config) {
25662     // these go here, so the translation tool can replace them..
25663     this.errors = {
25664         PwdEmpty: "Please type a password, and then retype it to confirm.",
25665         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25666         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25667         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25668         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25669         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25670         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25671         TooWeak: "Your password is Too Weak."
25672     },
25673     this.meterLabel = "Password strength:";
25674     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25675     this.meterClass = [
25676         "roo-password-meter-tooweak", 
25677         "roo-password-meter-weak", 
25678         "roo-password-meter-medium", 
25679         "roo-password-meter-strong", 
25680         "roo-password-meter-grey"
25681     ];
25682     
25683     this.errors = {};
25684     
25685     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25686 }
25687
25688 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25689     /**
25690      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25691      * {
25692      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25693      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25694      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25695      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25696      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25697      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25698      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25699      * })
25700      */
25701     // private
25702     
25703     meterWidth: 300,
25704     errorMsg :'',    
25705     errors: false,
25706     imageRoot: '/',
25707     /**
25708      * @cfg {String/Object} Label for the strength meter (defaults to
25709      * 'Password strength:')
25710      */
25711     // private
25712     meterLabel: '',
25713     /**
25714      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25715      * ['Weak', 'Medium', 'Strong'])
25716      */
25717     // private    
25718     pwdStrengths: false,    
25719     // private
25720     strength: 0,
25721     // private
25722     _lastPwd: null,
25723     // private
25724     kCapitalLetter: 0,
25725     kSmallLetter: 1,
25726     kDigit: 2,
25727     kPunctuation: 3,
25728     
25729     insecure: false,
25730     // private
25731     initEvents: function ()
25732     {
25733         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25734
25735         if (this.el.is('input[type=password]') && Roo.isSafari) {
25736             this.el.on('keydown', this.SafariOnKeyDown, this);
25737         }
25738
25739         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25740     },
25741     // private
25742     onRender: function (ct, position)
25743     {
25744         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25745         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25746         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25747
25748         this.trigger.createChild({
25749                    cn: [
25750                     {
25751                     //id: 'PwdMeter',
25752                     tag: 'div',
25753                     cls: 'roo-password-meter-grey col-xs-12',
25754                     style: {
25755                         //width: 0,
25756                         //width: this.meterWidth + 'px'                                                
25757                         }
25758                     },
25759                     {                            
25760                          cls: 'roo-password-meter-text'                          
25761                     }
25762                 ]            
25763         });
25764
25765          
25766         if (this.hideTrigger) {
25767             this.trigger.setDisplayed(false);
25768         }
25769         this.setSize(this.width || '', this.height || '');
25770     },
25771     // private
25772     onDestroy: function ()
25773     {
25774         if (this.trigger) {
25775             this.trigger.removeAllListeners();
25776             this.trigger.remove();
25777         }
25778         if (this.wrap) {
25779             this.wrap.remove();
25780         }
25781         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25782     },
25783     // private
25784     checkStrength: function ()
25785     {
25786         var pwd = this.inputEl().getValue();
25787         if (pwd == this._lastPwd) {
25788             return;
25789         }
25790
25791         var strength;
25792         if (this.ClientSideStrongPassword(pwd)) {
25793             strength = 3;
25794         } else if (this.ClientSideMediumPassword(pwd)) {
25795             strength = 2;
25796         } else if (this.ClientSideWeakPassword(pwd)) {
25797             strength = 1;
25798         } else {
25799             strength = 0;
25800         }
25801         
25802         Roo.log('strength1: ' + strength);
25803         
25804         //var pm = this.trigger.child('div/div/div').dom;
25805         var pm = this.trigger.child('div/div');
25806         pm.removeClass(this.meterClass);
25807         pm.addClass(this.meterClass[strength]);
25808                 
25809         
25810         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25811                 
25812         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25813         
25814         this._lastPwd = pwd;
25815     },
25816     reset: function ()
25817     {
25818         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25819         
25820         this._lastPwd = '';
25821         
25822         var pm = this.trigger.child('div/div');
25823         pm.removeClass(this.meterClass);
25824         pm.addClass('roo-password-meter-grey');        
25825         
25826         
25827         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25828         
25829         pt.innerHTML = '';
25830         this.inputEl().dom.type='password';
25831     },
25832     // private
25833     validateValue: function (value)
25834     {
25835         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25836             return false;
25837         }
25838         if (value.length == 0) {
25839             if (this.allowBlank) {
25840                 this.clearInvalid();
25841                 return true;
25842             }
25843
25844             this.markInvalid(this.errors.PwdEmpty);
25845             this.errorMsg = this.errors.PwdEmpty;
25846             return false;
25847         }
25848         
25849         if(this.insecure){
25850             return true;
25851         }
25852         
25853         if (!value.match(/[\x21-\x7e]+/)) {
25854             this.markInvalid(this.errors.PwdBadChar);
25855             this.errorMsg = this.errors.PwdBadChar;
25856             return false;
25857         }
25858         if (value.length < 6) {
25859             this.markInvalid(this.errors.PwdShort);
25860             this.errorMsg = this.errors.PwdShort;
25861             return false;
25862         }
25863         if (value.length > 16) {
25864             this.markInvalid(this.errors.PwdLong);
25865             this.errorMsg = this.errors.PwdLong;
25866             return false;
25867         }
25868         var strength;
25869         if (this.ClientSideStrongPassword(value)) {
25870             strength = 3;
25871         } else if (this.ClientSideMediumPassword(value)) {
25872             strength = 2;
25873         } else if (this.ClientSideWeakPassword(value)) {
25874             strength = 1;
25875         } else {
25876             strength = 0;
25877         }
25878
25879         
25880         if (strength < 2) {
25881             //this.markInvalid(this.errors.TooWeak);
25882             this.errorMsg = this.errors.TooWeak;
25883             //return false;
25884         }
25885         
25886         
25887         console.log('strength2: ' + strength);
25888         
25889         //var pm = this.trigger.child('div/div/div').dom;
25890         
25891         var pm = this.trigger.child('div/div');
25892         pm.removeClass(this.meterClass);
25893         pm.addClass(this.meterClass[strength]);
25894                 
25895         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25896                 
25897         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25898         
25899         this.errorMsg = ''; 
25900         return true;
25901     },
25902     // private
25903     CharacterSetChecks: function (type)
25904     {
25905         this.type = type;
25906         this.fResult = false;
25907     },
25908     // private
25909     isctype: function (character, type)
25910     {
25911         switch (type) {  
25912             case this.kCapitalLetter:
25913                 if (character >= 'A' && character <= 'Z') {
25914                     return true;
25915                 }
25916                 break;
25917             
25918             case this.kSmallLetter:
25919                 if (character >= 'a' && character <= 'z') {
25920                     return true;
25921                 }
25922                 break;
25923             
25924             case this.kDigit:
25925                 if (character >= '0' && character <= '9') {
25926                     return true;
25927                 }
25928                 break;
25929             
25930             case this.kPunctuation:
25931                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25932                     return true;
25933                 }
25934                 break;
25935             
25936             default:
25937                 return false;
25938         }
25939
25940     },
25941     // private
25942     IsLongEnough: function (pwd, size)
25943     {
25944         return !(pwd == null || isNaN(size) || pwd.length < size);
25945     },
25946     // private
25947     SpansEnoughCharacterSets: function (word, nb)
25948     {
25949         if (!this.IsLongEnough(word, nb))
25950         {
25951             return false;
25952         }
25953
25954         var characterSetChecks = new Array(
25955             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25956             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25957         );
25958         
25959         for (var index = 0; index < word.length; ++index) {
25960             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25961                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25962                     characterSetChecks[nCharSet].fResult = true;
25963                     break;
25964                 }
25965             }
25966         }
25967
25968         var nCharSets = 0;
25969         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25970             if (characterSetChecks[nCharSet].fResult) {
25971                 ++nCharSets;
25972             }
25973         }
25974
25975         if (nCharSets < nb) {
25976             return false;
25977         }
25978         return true;
25979     },
25980     // private
25981     ClientSideStrongPassword: function (pwd)
25982     {
25983         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25984     },
25985     // private
25986     ClientSideMediumPassword: function (pwd)
25987     {
25988         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25989     },
25990     // private
25991     ClientSideWeakPassword: function (pwd)
25992     {
25993         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25994     }
25995           
25996 });
25997 Roo.htmleditor = {};
25998  
25999 /**
26000  * @class Roo.htmleditor.Filter
26001  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26002  * @cfg {DomElement} node The node to iterate and filter
26003  * @cfg {boolean|String|Array} tag Tags to replace 
26004  * @constructor
26005  * Create a new Filter.
26006  * @param {Object} config Configuration options
26007  */
26008
26009
26010
26011 Roo.htmleditor.Filter = function(cfg) {
26012     Roo.apply(this.cfg);
26013     // this does not actually call walk as it's really just a abstract class
26014 }
26015
26016
26017 Roo.htmleditor.Filter.prototype = {
26018     
26019     node: false,
26020     
26021     tag: false,
26022
26023     // overrride to do replace comments.
26024     replaceComment : false,
26025     
26026     // overrride to do replace or do stuff with tags..
26027     replaceTag : false,
26028     
26029     walk : function(dom)
26030     {
26031         Roo.each( Array.from(dom.childNodes), function( e ) {
26032             switch(true) {
26033                 
26034                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
26035                     this.replaceComment(e);
26036                     return;
26037                 
26038                 case e.nodeType != 1: //not a node.
26039                     return;
26040                 
26041                 case this.tag === true: // everything
26042                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26043                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26044                     if (this.replaceTag && false === this.replaceTag(e)) {
26045                         return;
26046                     }
26047                     if (e.hasChildNodes()) {
26048                         this.walk(e);
26049                     }
26050                     return;
26051                 
26052                 default:    // tags .. that do not match.
26053                     if (e.hasChildNodes()) {
26054                         this.walk(e);
26055                     }
26056             }
26057             
26058         }, this);
26059         
26060     }
26061 }; 
26062
26063 /**
26064  * @class Roo.htmleditor.FilterAttributes
26065  * clean attributes and  styles including http:// etc.. in attribute
26066  * @constructor
26067 * Run a new Attribute Filter
26068 * @param {Object} config Configuration options
26069  */
26070 Roo.htmleditor.FilterAttributes = function(cfg)
26071 {
26072     Roo.apply(this, cfg);
26073     this.attrib_black = this.attrib_black || [];
26074     this.attrib_white = this.attrib_white || [];
26075
26076     this.attrib_clean = this.attrib_clean || [];
26077     this.style_white = this.style_white || [];
26078     this.style_black = this.style_black || [];
26079     this.walk(cfg.node);
26080 }
26081
26082 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26083 {
26084     tag: true, // all tags
26085     
26086     attrib_black : false, // array
26087     attrib_clean : false,
26088     attrib_white : false,
26089
26090     style_white : false,
26091     style_black : false,
26092      
26093      
26094     replaceTag : function(node)
26095     {
26096         if (!node.attributes || !node.attributes.length) {
26097             return true;
26098         }
26099         
26100         for (var i = node.attributes.length-1; i > -1 ; i--) {
26101             var a = node.attributes[i];
26102             //console.log(a);
26103             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26104                 node.removeAttribute(a.name);
26105                 continue;
26106             }
26107             
26108             
26109             
26110             if (a.name.toLowerCase().substr(0,2)=='on')  {
26111                 node.removeAttribute(a.name);
26112                 continue;
26113             }
26114             
26115             
26116             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26117                 node.removeAttribute(a.name);
26118                 continue;
26119             }
26120             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26121                 this.cleanAttr(node,a.name,a.value); // fixme..
26122                 continue;
26123             }
26124             if (a.name == 'style') {
26125                 this.cleanStyle(node,a.name,a.value);
26126                 continue;
26127             }
26128             /// clean up MS crap..
26129             // tecnically this should be a list of valid class'es..
26130             
26131             
26132             if (a.name == 'class') {
26133                 if (a.value.match(/^Mso/)) {
26134                     node.removeAttribute('class');
26135                 }
26136                 
26137                 if (a.value.match(/^body$/)) {
26138                     node.removeAttribute('class');
26139                 }
26140                 continue;
26141             }
26142             
26143             
26144             // style cleanup!?
26145             // class cleanup?
26146             
26147         }
26148         return true; // clean children
26149     },
26150         
26151     cleanAttr: function(node, n,v)
26152     {
26153         
26154         if (v.match(/^\./) || v.match(/^\//)) {
26155             return;
26156         }
26157         if (v.match(/^(http|https):\/\//)
26158             || v.match(/^mailto:/) 
26159             || v.match(/^ftp:/)
26160             || v.match(/^data:/)
26161             ) {
26162             return;
26163         }
26164         if (v.match(/^#/)) {
26165             return;
26166         }
26167         if (v.match(/^\{/)) { // allow template editing.
26168             return;
26169         }
26170 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26171         node.removeAttribute(n);
26172         
26173     },
26174     cleanStyle : function(node,  n,v)
26175     {
26176         if (v.match(/expression/)) { //XSS?? should we even bother..
26177             node.removeAttribute(n);
26178             return;
26179         }
26180         
26181         var parts = v.split(/;/);
26182         var clean = [];
26183         
26184         Roo.each(parts, function(p) {
26185             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26186             if (!p.length) {
26187                 return true;
26188             }
26189             var l = p.split(':').shift().replace(/\s+/g,'');
26190             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26191             
26192             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26193                 return true;
26194             }
26195             //Roo.log()
26196             // only allow 'c whitelisted system attributes'
26197             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26198                 return true;
26199             }
26200             
26201             
26202             clean.push(p);
26203             return true;
26204         },this);
26205         if (clean.length) { 
26206             node.setAttribute(n, clean.join(';'));
26207         } else {
26208             node.removeAttribute(n);
26209         }
26210         
26211     }
26212         
26213         
26214         
26215     
26216 });/**
26217  * @class Roo.htmleditor.FilterBlack
26218  * remove blacklisted elements.
26219  * @constructor
26220  * Run a new Blacklisted Filter
26221  * @param {Object} config Configuration options
26222  */
26223
26224 Roo.htmleditor.FilterBlack = function(cfg)
26225 {
26226     Roo.apply(this, cfg);
26227     this.walk(cfg.node);
26228 }
26229
26230 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26231 {
26232     tag : true, // all elements.
26233    
26234     replaceTag : function(n)
26235     {
26236         n.parentNode.removeChild(n);
26237     }
26238 });
26239 /**
26240  * @class Roo.htmleditor.FilterComment
26241  * remove comments.
26242  * @constructor
26243 * Run a new Comments Filter
26244 * @param {Object} config Configuration options
26245  */
26246 Roo.htmleditor.FilterComment = function(cfg)
26247 {
26248     this.walk(cfg.node);
26249 }
26250
26251 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26252 {
26253   
26254     replaceComment : function(n)
26255     {
26256         n.parentNode.removeChild(n);
26257     }
26258 });/**
26259  * @class Roo.htmleditor.FilterKeepChildren
26260  * remove tags but keep children
26261  * @constructor
26262  * Run a new Keep Children Filter
26263  * @param {Object} config Configuration options
26264  */
26265
26266 Roo.htmleditor.FilterKeepChildren = function(cfg)
26267 {
26268     Roo.apply(this, cfg);
26269     if (this.tag === false) {
26270         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26271     }
26272     this.walk(cfg.node);
26273 }
26274
26275 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26276 {
26277     
26278   
26279     replaceTag : function(node)
26280     {
26281         // walk children...
26282         //Roo.log(node);
26283         var ar = Array.from(node.childNodes);
26284         //remove first..
26285         for (var i = 0; i < ar.length; i++) {
26286             if (ar[i].nodeType == 1) {
26287                 if (
26288                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
26289                     || // array and it matches
26290                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
26291                 ) {
26292                     this.replaceTag(ar[i]); // child is blacklisted as well...
26293                     continue;
26294                 }
26295             }
26296         }  
26297         ar = Array.from(node.childNodes);
26298         for (var i = 0; i < ar.length; i++) {
26299          
26300             node.removeChild(ar[i]);
26301             // what if we need to walk these???
26302             node.parentNode.insertBefore(ar[i], node);
26303             if (this.tag !== false) {
26304                 this.walk(ar[i]);
26305                 
26306             }
26307         }
26308         node.parentNode.removeChild(node);
26309         return false; // don't walk children
26310         
26311         
26312     }
26313 });/**
26314  * @class Roo.htmleditor.FilterParagraph
26315  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26316  * like on 'push' to remove the <p> tags and replace them with line breaks.
26317  * @constructor
26318  * Run a new Paragraph Filter
26319  * @param {Object} config Configuration options
26320  */
26321
26322 Roo.htmleditor.FilterParagraph = function(cfg)
26323 {
26324     // no need to apply config.
26325     this.walk(cfg.node);
26326 }
26327
26328 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26329 {
26330     
26331      
26332     tag : 'P',
26333     
26334      
26335     replaceTag : function(node)
26336     {
26337         
26338         if (node.childNodes.length == 1 &&
26339             node.childNodes[0].nodeType == 3 &&
26340             node.childNodes[0].textContent.trim().length < 1
26341             ) {
26342             // remove and replace with '<BR>';
26343             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26344             return false; // no need to walk..
26345         }
26346         var ar = Array.from(node.childNodes);
26347         for (var i = 0; i < ar.length; i++) {
26348             node.removeChild(ar[i]);
26349             // what if we need to walk these???
26350             node.parentNode.insertBefore(ar[i], node);
26351         }
26352         // now what about this?
26353         // <p> &nbsp; </p>
26354         
26355         // double BR.
26356         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26357         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26358         node.parentNode.removeChild(node);
26359         
26360         return false;
26361
26362     }
26363     
26364 });/**
26365  * @class Roo.htmleditor.FilterSpan
26366  * filter span's with no attributes out..
26367  * @constructor
26368  * Run a new Span Filter
26369  * @param {Object} config Configuration options
26370  */
26371
26372 Roo.htmleditor.FilterSpan = function(cfg)
26373 {
26374     // no need to apply config.
26375     this.walk(cfg.node);
26376 }
26377
26378 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26379 {
26380      
26381     tag : 'SPAN',
26382      
26383  
26384     replaceTag : function(node)
26385     {
26386         if (node.attributes && node.attributes.length > 0) {
26387             return true; // walk if there are any.
26388         }
26389         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26390         return false;
26391      
26392     }
26393     
26394 });/**
26395  * @class Roo.htmleditor.FilterTableWidth
26396   try and remove table width data - as that frequently messes up other stuff.
26397  * 
26398  *      was cleanTableWidths.
26399  *
26400  * Quite often pasting from word etc.. results in tables with column and widths.
26401  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26402  *
26403  * @constructor
26404  * Run a new Table Filter
26405  * @param {Object} config Configuration options
26406  */
26407
26408 Roo.htmleditor.FilterTableWidth = function(cfg)
26409 {
26410     // no need to apply config.
26411     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26412     this.walk(cfg.node);
26413 }
26414
26415 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26416 {
26417      
26418      
26419     
26420     replaceTag: function(node) {
26421         
26422         
26423       
26424         if (node.hasAttribute('width')) {
26425             node.removeAttribute('width');
26426         }
26427         
26428          
26429         if (node.hasAttribute("style")) {
26430             // pretty basic...
26431             
26432             var styles = node.getAttribute("style").split(";");
26433             var nstyle = [];
26434             Roo.each(styles, function(s) {
26435                 if (!s.match(/:/)) {
26436                     return;
26437                 }
26438                 var kv = s.split(":");
26439                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26440                     return;
26441                 }
26442                 // what ever is left... we allow.
26443                 nstyle.push(s);
26444             });
26445             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26446             if (!nstyle.length) {
26447                 node.removeAttribute('style');
26448             }
26449         }
26450         
26451         return true; // continue doing children..
26452     }
26453 });/**
26454  * @class Roo.htmleditor.FilterWord
26455  * try and clean up all the mess that Word generates.
26456  * 
26457  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
26458  
26459  * @constructor
26460  * Run a new Span Filter
26461  * @param {Object} config Configuration options
26462  */
26463
26464 Roo.htmleditor.FilterWord = function(cfg)
26465 {
26466     // no need to apply config.
26467     this.replaceDocBullets(cfg.node);
26468     
26469    // this.walk(cfg.node);
26470     
26471     
26472 }
26473
26474 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
26475 {
26476     tag: true,
26477      
26478     
26479     /**
26480      * Clean up MS wordisms...
26481      */
26482     replaceTag : function(node)
26483     {
26484          
26485         // no idea what this does - span with text, replaceds with just text.
26486         if(
26487                 node.nodeName == 'SPAN' &&
26488                 !node.hasAttributes() &&
26489                 node.childNodes.length == 1 &&
26490                 node.firstChild.nodeName == "#text"  
26491         ) {
26492             var textNode = node.firstChild;
26493             node.removeChild(textNode);
26494             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26495                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26496             }
26497             node.parentNode.insertBefore(textNode, node);
26498             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26499                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26500             }
26501             
26502             node.parentNode.removeChild(node);
26503             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
26504         }
26505         
26506    
26507         
26508         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26509             node.parentNode.removeChild(node);
26510             return false; // dont do chidlren
26511         }
26512         //Roo.log(node.tagName);
26513         // remove - but keep children..
26514         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26515             //Roo.log('-- removed');
26516             while (node.childNodes.length) {
26517                 var cn = node.childNodes[0];
26518                 node.removeChild(cn);
26519                 node.parentNode.insertBefore(cn, node);
26520                 // move node to parent - and clean it..
26521                 if (cn.nodeType == 1) {
26522                     this.replaceTag(cn);
26523                 }
26524                 
26525             }
26526             node.parentNode.removeChild(node);
26527             /// no need to iterate chidlren = it's got none..
26528             //this.iterateChildren(node, this.cleanWord);
26529             return false; // no need to iterate children.
26530         }
26531         // clean styles
26532         if (node.className.length) {
26533             
26534             var cn = node.className.split(/\W+/);
26535             var cna = [];
26536             Roo.each(cn, function(cls) {
26537                 if (cls.match(/Mso[a-zA-Z]+/)) {
26538                     return;
26539                 }
26540                 cna.push(cls);
26541             });
26542             node.className = cna.length ? cna.join(' ') : '';
26543             if (!cna.length) {
26544                 node.removeAttribute("class");
26545             }
26546         }
26547         
26548         if (node.hasAttribute("lang")) {
26549             node.removeAttribute("lang");
26550         }
26551         
26552         if (node.hasAttribute("style")) {
26553             
26554             var styles = node.getAttribute("style").split(";");
26555             var nstyle = [];
26556             Roo.each(styles, function(s) {
26557                 if (!s.match(/:/)) {
26558                     return;
26559                 }
26560                 var kv = s.split(":");
26561                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26562                     return;
26563                 }
26564                 // what ever is left... we allow.
26565                 nstyle.push(s);
26566             });
26567             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26568             if (!nstyle.length) {
26569                 node.removeAttribute('style');
26570             }
26571         }
26572         return true; // do children
26573         
26574         
26575         
26576     },
26577     
26578     styleToObject: function(node)
26579     {
26580         var styles = (node.getAttribute("style") || '').split(";");
26581         var ret = {};
26582         Roo.each(styles, function(s) {
26583             if (!s.match(/:/)) {
26584                 return;
26585             }
26586             var kv = s.split(":");
26587              
26588             // what ever is left... we allow.
26589             ret[kv[0].trim()] = kv[1];
26590         });
26591         return ret;
26592     },
26593     
26594     
26595     replaceDocBullets : function(doc)
26596     {
26597         // this is a bit odd - but it appears some indents use ql-indent-1
26598         
26599         var listpara = doc.getElementsByClassName('ql-indent-1');
26600         while(listpara.length) {
26601             this.replaceDocBullet(listpara.item(0));
26602         }
26603         
26604         var listpara = doc.getElementsByClassName('MsoListParagraph');
26605         while(listpara.length) {
26606             this.replaceDocBullet(listpara.item(0));
26607         }
26608     },
26609     
26610     replaceDocBullet : function(p)
26611     {
26612         // gather all the siblings.
26613         var ns = p,
26614             parent = p.parentNode,
26615             doc = parent.ownerDocument,
26616             items = [];
26617             
26618             
26619         while (ns) {
26620             if (ns.nodeType != 1) {
26621                 ns = ns.nextSibling;
26622                 continue;
26623             }
26624             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
26625                 break;
26626             }
26627             items.push(ns);
26628             ns = ns.nextSibling;
26629         }
26630         
26631         
26632         var ul = parent.ownerDocument.createElement('ul'); // what about number lists...
26633         parent.insertBefore(ul, p);
26634         var lvl = 0;
26635         var stack = [ ul ];
26636         var last_li = false;
26637         
26638         items.forEach(function(n, ipos) {
26639             //Roo.log("got innertHMLT=" + n.innerHTML);
26640             
26641             var spans = n.getElementsByTagName('span');
26642             if (!spans.length) {
26643                 //Roo.log("No spans found");
26644
26645                 parent.removeChild(n);
26646                 return; // skip it...
26647             }
26648            
26649                 
26650             
26651             var style = {};
26652             for(var i = 0; i < spans.length; i++) {
26653             
26654                 style = this.styleToObject(spans[i]);
26655                 if (typeof(style['mso-list']) == 'undefined') {
26656                     continue;
26657                 }
26658                 
26659                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
26660                 break;
26661             }
26662             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
26663             style = this.styleToObject(n); // mo-list is from the parent node.
26664             if (typeof(style['mso-list']) == 'undefined') {
26665                 //Roo.log("parent is missing level");
26666                 parent.removeChild(n);
26667                 return;
26668             }
26669             
26670             var nlvl =   (style['mso-list'].split(' ')[1].replace(/level/,'') *1) - 1  ;
26671             
26672             
26673             
26674             if (nlvl > lvl) {
26675                 //new indent
26676                 var nul = doc.createElement('ul'); // what about number lists...
26677                 last_li.appendChild(nul);
26678                 stack[nlvl] = nul;
26679                 
26680             }
26681             lvl = nlvl;
26682             
26683             var nli = stack[nlvl].appendChild(doc.createElement('li'));
26684             last_li = nli;
26685             nli.innerHTML = n.innerHTML;
26686             //Roo.log("innerHTML = " + n.innerHTML);
26687             parent.removeChild(n);
26688             
26689             // copy children of p into nli
26690             /*while(n.firstChild) {
26691                 var fc = n.firstChild;
26692                 n.removeChild(fc);
26693                 nli.appendChild(fc);
26694             }*/
26695              
26696             
26697         },this);
26698         
26699         
26700         
26701         
26702     }
26703     
26704     
26705     
26706 });
26707 /**
26708  * @class Roo.htmleditor.FilterStyleToTag
26709  * part of the word stuff... - certain 'styles' should be converted to tags.
26710  * eg.
26711  *   font-weight: bold -> bold
26712  *   ?? super / subscrit etc..
26713  * 
26714  * @constructor
26715 * Run a new style to tag filter.
26716 * @param {Object} config Configuration options
26717  */
26718 Roo.htmleditor.FilterStyleToTag = function(cfg)
26719 {
26720     
26721     this.tags = {
26722         B  : [ 'fontWeight' , 'bold'],
26723         I :  [ 'fontStyle' , 'italic'],
26724         //pre :  [ 'font-style' , 'italic'],
26725         // h1.. h6 ?? font-size?
26726         SUP : [ 'verticalAlign' , 'super' ],
26727         SUB : [ 'verticalAlign' , 'sub' ]
26728         
26729         
26730     };
26731     
26732     Roo.apply(this, cfg);
26733      
26734     
26735     this.walk(cfg.node);
26736     
26737     
26738     
26739 }
26740
26741
26742 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
26743 {
26744     tag: true, // all tags
26745     
26746     tags : false,
26747     
26748     
26749     replaceTag : function(node)
26750     {
26751         
26752         
26753         if (node.getAttribute("style") === null) {
26754             return true;
26755         }
26756         var inject = [];
26757         for (var k in this.tags) {
26758             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
26759                 inject.push(k);
26760                 node.style.removeProperty(this.tags[k][0]);
26761             }
26762         }
26763         if (!inject.length) {
26764             return true; 
26765         }
26766         var cn = Array.from(node.childNodes);
26767         var nn = node;
26768         Roo.each(inject, function(t) {
26769             var nc = node.ownerDocument.createElement(t);
26770             nn.appendChild(nc);
26771             nn = nc;
26772         });
26773         for(var i = 0;i < cn.length;cn++) {
26774             node.removeChild(cn[i]);
26775             nn.appendChild(cn[i]);
26776         }
26777         return true /// iterate thru
26778     }
26779     
26780 })/**
26781  * @class Roo.htmleditor.FilterLongBr
26782  * BR/BR/BR - keep a maximum of 2...
26783  * @constructor
26784  * Run a new Long BR Filter
26785  * @param {Object} config Configuration options
26786  */
26787
26788 Roo.htmleditor.FilterLongBr = function(cfg)
26789 {
26790     // no need to apply config.
26791     this.walk(cfg.node);
26792 }
26793
26794 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
26795 {
26796     
26797      
26798     tag : 'BR',
26799     
26800      
26801     replaceTag : function(node)
26802     {
26803         
26804         var ps = node.nextSibling;
26805         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26806             ps = ps.nextSibling;
26807         }
26808         
26809         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
26810             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
26811             return false;
26812         }
26813         
26814         if (!ps || ps.nodeType != 1) {
26815             return false;
26816         }
26817         
26818         if (!ps || ps.tagName != 'BR') {
26819            
26820             return false;
26821         }
26822         
26823         
26824         
26825         
26826         
26827         if (!node.previousSibling) {
26828             return false;
26829         }
26830         var ps = node.previousSibling;
26831         
26832         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26833             ps = ps.previousSibling;
26834         }
26835         if (!ps || ps.nodeType != 1) {
26836             return false;
26837         }
26838         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
26839         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
26840             return false;
26841         }
26842         
26843         node.parentNode.removeChild(node); // remove me...
26844         
26845         return false; // no need to do children
26846
26847     }
26848     
26849 }); 
26850
26851 /**
26852  * @class Roo.htmleditor.FilterBlock
26853  * removes id / data-block and contenteditable that are associated with blocks
26854  * usage should be done on a cloned copy of the dom
26855  * @constructor
26856 * Run a new Attribute Filter { node : xxxx }}
26857 * @param {Object} config Configuration options
26858  */
26859 Roo.htmleditor.FilterBlock = function(cfg)
26860 {
26861     Roo.apply(this, cfg);
26862     var qa = cfg.node.querySelectorAll;
26863     this.removeAttributes('data-block');
26864     this.removeAttributes('contenteditable');
26865     this.removeAttributes('id');
26866     
26867 }
26868
26869 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
26870 {
26871     node: true, // all tags
26872      
26873      
26874     removeAttributes : function(attr)
26875     {
26876         var ar = this.node.querySelectorAll('*[' + attr + ']');
26877         for (var i =0;i<ar.length;i++) {
26878             ar[i].removeAttribute(attr);
26879         }
26880     }
26881         
26882         
26883         
26884     
26885 });
26886 /**
26887  * @class Roo.htmleditor.KeyEnter
26888  * Handle Enter press..
26889  * @cfg {Roo.HtmlEditorCore} core the editor.
26890  * @constructor
26891  * Create a new Filter.
26892  * @param {Object} config Configuration options
26893  */
26894
26895
26896
26897
26898
26899 Roo.htmleditor.KeyEnter = function(cfg) {
26900     Roo.apply(this, cfg);
26901     // this does not actually call walk as it's really just a abstract class
26902  
26903     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
26904 }
26905
26906 //Roo.htmleditor.KeyEnter.i = 0;
26907
26908
26909 Roo.htmleditor.KeyEnter.prototype = {
26910     
26911     core : false,
26912     
26913     keypress : function(e)
26914     {
26915         if (e.charCode != 13 && e.charCode != 10) {
26916             Roo.log([e.charCode,e]);
26917             return true;
26918         }
26919         e.preventDefault();
26920         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
26921         var doc = this.core.doc;
26922           //add a new line
26923        
26924     
26925         var sel = this.core.getSelection();
26926         var range = sel.getRangeAt(0);
26927         var n = range.commonAncestorContainer;
26928         var pc = range.closest([ 'ol', 'ul']);
26929         var pli = range.closest('li');
26930         if (!pc || e.ctrlKey) {
26931             sel.insertNode('br', 'after'); 
26932          
26933             this.core.undoManager.addEvent();
26934             this.core.fireEditorEvent(e);
26935             return false;
26936         }
26937         
26938         // deal with <li> insetion
26939         if (pli.innerText.trim() == '' &&
26940             pli.previousSibling &&
26941             pli.previousSibling.nodeName == 'LI' &&
26942             pli.previousSibling.innerText.trim() ==  '') {
26943             pli.parentNode.removeChild(pli.previousSibling);
26944             sel.cursorAfter(pc);
26945             this.core.undoManager.addEvent();
26946             this.core.fireEditorEvent(e);
26947             return false;
26948         }
26949     
26950         var li = doc.createElement('LI');
26951         li.innerHTML = '&nbsp;';
26952         if (!pli || !pli.firstSibling) {
26953             pc.appendChild(li);
26954         } else {
26955             pli.parentNode.insertBefore(li, pli.firstSibling);
26956         }
26957         sel.cursorText (li.firstChild);
26958       
26959         this.core.undoManager.addEvent();
26960         this.core.fireEditorEvent(e);
26961
26962         return false;
26963         
26964     
26965         
26966         
26967          
26968     }
26969 };
26970      
26971 /**
26972  * @class Roo.htmleditor.Block
26973  * Base class for html editor blocks - do not use it directly .. extend it..
26974  * @cfg {DomElement} node The node to apply stuff to.
26975  * @cfg {String} friendly_name the name that appears in the context bar about this block
26976  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
26977  
26978  * @constructor
26979  * Create a new Filter.
26980  * @param {Object} config Configuration options
26981  */
26982
26983 Roo.htmleditor.Block  = function(cfg)
26984 {
26985     // do nothing .. should not be called really.
26986 }
26987 /**
26988  * factory method to get the block from an element (using cache if necessary)
26989  * @static
26990  * @param {HtmlElement} the dom element
26991  */
26992 Roo.htmleditor.Block.factory = function(node)
26993 {
26994     var cc = Roo.htmleditor.Block.cache;
26995     var id = Roo.get(node).id;
26996     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
26997         Roo.htmleditor.Block.cache[id].readElement(node);
26998         return Roo.htmleditor.Block.cache[id];
26999     }
27000     var db  = node.getAttribute('data-block');
27001     if (!db) {
27002         db = node.nodeName.toLowerCase().toUpperCaseFirst();
27003     }
27004     var cls = Roo.htmleditor['Block' + db];
27005     if (typeof(cls) == 'undefined') {
27006         //Roo.log(node.getAttribute('data-block'));
27007         Roo.log("OOps missing block : " + 'Block' + db);
27008         return false;
27009     }
27010     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
27011     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
27012 };
27013
27014 /**
27015  * initalize all Elements from content that are 'blockable'
27016  * @static
27017  * @param the body element
27018  */
27019 Roo.htmleditor.Block.initAll = function(body, type)
27020 {
27021     if (typeof(type) == 'undefined') {
27022         var ia = Roo.htmleditor.Block.initAll;
27023         ia(body,'table');
27024         ia(body,'td');
27025         ia(body,'figure');
27026         return;
27027     }
27028     Roo.each(Roo.get(body).query(type), function(e) {
27029         Roo.htmleditor.Block.factory(e);    
27030     },this);
27031 };
27032 // question goes here... do we need to clear out this cache sometimes?
27033 // or show we make it relivant to the htmleditor.
27034 Roo.htmleditor.Block.cache = {};
27035
27036 Roo.htmleditor.Block.prototype = {
27037     
27038     node : false,
27039     
27040      // used by context menu
27041     friendly_name : 'Based Block',
27042     
27043     // text for button to delete this element
27044     deleteTitle : false,
27045     
27046     context : false,
27047     /**
27048      * Update a node with values from this object
27049      * @param {DomElement} node
27050      */
27051     updateElement : function(node)
27052     {
27053         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
27054     },
27055      /**
27056      * convert to plain HTML for calling insertAtCursor..
27057      */
27058     toHTML : function()
27059     {
27060         return Roo.DomHelper.markup(this.toObject());
27061     },
27062     /**
27063      * used by readEleemnt to extract data from a node
27064      * may need improving as it's pretty basic
27065      
27066      * @param {DomElement} node
27067      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
27068      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
27069      * @param {String} style the style property - eg. text-align
27070      */
27071     getVal : function(node, tag, attr, style)
27072     {
27073         var n = node;
27074         if (tag !== true && n.tagName != tag.toUpperCase()) {
27075             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
27076             // but kiss for now.
27077             n = node.getElementsByTagName(tag).item(0);
27078         }
27079         if (!n) {
27080             return '';
27081         }
27082         if (attr === false) {
27083             return n;
27084         }
27085         if (attr == 'html') {
27086             return n.innerHTML;
27087         }
27088         if (attr == 'style') {
27089             return n.style[style]; 
27090         }
27091         
27092         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
27093             
27094     },
27095     /**
27096      * create a DomHelper friendly object - for use with 
27097      * Roo.DomHelper.markup / overwrite / etc..
27098      * (override this)
27099      */
27100     toObject : function()
27101     {
27102         return {};
27103     },
27104       /**
27105      * Read a node that has a 'data-block' property - and extract the values from it.
27106      * @param {DomElement} node - the node
27107      */
27108     readElement : function(node)
27109     {
27110         
27111     } 
27112     
27113     
27114 };
27115
27116  
27117
27118 /**
27119  * @class Roo.htmleditor.BlockFigure
27120  * Block that has an image and a figcaption
27121  * @cfg {String} image_src the url for the image
27122  * @cfg {String} align (left|right) alignment for the block default left
27123  * @cfg {String} caption the text to appear below  (and in the alt tag)
27124  * @cfg {String} caption_display (block|none) display or not the caption
27125  * @cfg {String|number} image_width the width of the image number or %?
27126  * @cfg {String|number} image_height the height of the image number or %?
27127  * 
27128  * @constructor
27129  * Create a new Filter.
27130  * @param {Object} config Configuration options
27131  */
27132
27133 Roo.htmleditor.BlockFigure = function(cfg)
27134 {
27135     if (cfg.node) {
27136         this.readElement(cfg.node);
27137         this.updateElement(cfg.node);
27138     }
27139     Roo.apply(this, cfg);
27140 }
27141 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
27142  
27143     
27144     // setable values.
27145     image_src: '',
27146     align: 'center',
27147     caption : '',
27148     caption_display : 'block',
27149     width : '100%',
27150     cls : '',
27151     href: '',
27152     video_url : '',
27153     
27154     // margin: '2%', not used
27155     
27156     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
27157
27158     
27159     // used by context menu
27160     friendly_name : 'Image with caption',
27161     deleteTitle : "Delete Image and Caption",
27162     
27163     contextMenu : function(toolbar)
27164     {
27165         
27166         var block = function() {
27167             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
27168         };
27169         
27170         
27171         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
27172         
27173         var syncValue = toolbar.editorcore.syncValue;
27174         
27175         var fields = {};
27176         
27177         return [
27178              {
27179                 xtype : 'TextItem',
27180                 text : "Source: ",
27181                 xns : rooui.Toolbar  //Boostrap?
27182             },
27183             {
27184                 xtype : 'Button',
27185                 text: 'Change Image URL',
27186                  
27187                 listeners : {
27188                     click: function (btn, state)
27189                     {
27190                         var b = block();
27191                         
27192                         Roo.MessageBox.show({
27193                             title : "Image Source URL",
27194                             msg : "Enter the url for the image",
27195                             buttons: Roo.MessageBox.OKCANCEL,
27196                             fn: function(btn, val){
27197                                 if (btn != 'ok') {
27198                                     return;
27199                                 }
27200                                 b.image_src = val;
27201                                 b.updateElement();
27202                                 syncValue();
27203                                 toolbar.editorcore.onEditorEvent();
27204                             },
27205                             minWidth:250,
27206                             prompt:true,
27207                             //multiline: multiline,
27208                             modal : true,
27209                             value : b.image_src
27210                         });
27211                     }
27212                 },
27213                 xns : rooui.Toolbar
27214             },
27215          
27216             {
27217                 xtype : 'Button',
27218                 text: 'Change Link URL',
27219                  
27220                 listeners : {
27221                     click: function (btn, state)
27222                     {
27223                         var b = block();
27224                         
27225                         Roo.MessageBox.show({
27226                             title : "Link URL",
27227                             msg : "Enter the url for the link - leave blank to have no link",
27228                             buttons: Roo.MessageBox.OKCANCEL,
27229                             fn: function(btn, val){
27230                                 if (btn != 'ok') {
27231                                     return;
27232                                 }
27233                                 b.href = val;
27234                                 b.updateElement();
27235                                 syncValue();
27236                                 toolbar.editorcore.onEditorEvent();
27237                             },
27238                             minWidth:250,
27239                             prompt:true,
27240                             //multiline: multiline,
27241                             modal : true,
27242                             value : b.href
27243                         });
27244                     }
27245                 },
27246                 xns : rooui.Toolbar
27247             },
27248             {
27249                 xtype : 'Button',
27250                 text: 'Show Video URL',
27251                  
27252                 listeners : {
27253                     click: function (btn, state)
27254                     {
27255                         Roo.MessageBox.alert("Video URL",
27256                             block().video_url == '' ? 'This image is not linked ot a video' :
27257                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
27258                     }
27259                 },
27260                 xns : rooui.Toolbar
27261             },
27262             
27263             
27264             {
27265                 xtype : 'TextItem',
27266                 text : "Width: ",
27267                 xns : rooui.Toolbar  //Boostrap?
27268             },
27269             {
27270                 xtype : 'ComboBox',
27271                 allowBlank : false,
27272                 displayField : 'val',
27273                 editable : true,
27274                 listWidth : 100,
27275                 triggerAction : 'all',
27276                 typeAhead : true,
27277                 valueField : 'val',
27278                 width : 70,
27279                 name : 'width',
27280                 listeners : {
27281                     select : function (combo, r, index)
27282                     {
27283                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27284                         var b = block();
27285                         b.width = r.get('val');
27286                         b.updateElement();
27287                         syncValue();
27288                         toolbar.editorcore.onEditorEvent();
27289                     }
27290                 },
27291                 xns : rooui.form,
27292                 store : {
27293                     xtype : 'SimpleStore',
27294                     data : [
27295                         ['50%'],
27296                         ['80%'],
27297                         ['100%']
27298                     ],
27299                     fields : [ 'val'],
27300                     xns : Roo.data
27301                 }
27302             },
27303             {
27304                 xtype : 'TextItem',
27305                 text : "Align: ",
27306                 xns : rooui.Toolbar  //Boostrap?
27307             },
27308             {
27309                 xtype : 'ComboBox',
27310                 allowBlank : false,
27311                 displayField : 'val',
27312                 editable : true,
27313                 listWidth : 100,
27314                 triggerAction : 'all',
27315                 typeAhead : true,
27316                 valueField : 'val',
27317                 width : 70,
27318                 name : 'align',
27319                 listeners : {
27320                     select : function (combo, r, index)
27321                     {
27322                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27323                         var b = block();
27324                         b.align = r.get('val');
27325                         b.updateElement();
27326                         syncValue();
27327                         toolbar.editorcore.onEditorEvent();
27328                     }
27329                 },
27330                 xns : rooui.form,
27331                 store : {
27332                     xtype : 'SimpleStore',
27333                     data : [
27334                         ['left'],
27335                         ['right'],
27336                         ['center']
27337                     ],
27338                     fields : [ 'val'],
27339                     xns : Roo.data
27340                 }
27341             },
27342             
27343             
27344             {
27345                 xtype : 'Button',
27346                 text: 'Hide Caption',
27347                 name : 'caption_display',
27348                 pressed : false,
27349                 enableToggle : true,
27350                 setValue : function(v) {
27351                     // this trigger toggle.
27352                      
27353                     this.setText(v ? "Hide Caption" : "Show Caption");
27354                     this.setPressed(v != 'block');
27355                 },
27356                 listeners : {
27357                     toggle: function (btn, state)
27358                     {
27359                         var b  = block();
27360                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
27361                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
27362                         b.updateElement();
27363                         syncValue();
27364                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27365                         toolbar.editorcore.onEditorEvent();
27366                     }
27367                 },
27368                 xns : rooui.Toolbar
27369             }
27370         ];
27371         
27372     },
27373     /**
27374      * create a DomHelper friendly object - for use with
27375      * Roo.DomHelper.markup / overwrite / etc..
27376      */
27377     toObject : function()
27378     {
27379         var d = document.createElement('div');
27380         d.innerHTML = this.caption;
27381         
27382         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
27383         
27384         var iw = this.align == 'center' ? this.width : '100%';
27385         var img =   {
27386             tag : 'img',
27387             contenteditable : 'false',
27388             src : this.image_src,
27389             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
27390             style: {
27391                 width : iw,
27392                 maxWidth : iw + ' !important', // this is not getting rendered?
27393                 margin : m  
27394                 
27395             }
27396         };
27397         /*
27398         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
27399                     '<a href="{2}">' + 
27400                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
27401                     '</a>' + 
27402                 '</div>',
27403         */
27404                 
27405         if (this.href.length > 0) {
27406             img = {
27407                 tag : 'a',
27408                 href: this.href,
27409                 contenteditable : 'true',
27410                 cn : [
27411                     img
27412                 ]
27413             };
27414         }
27415         
27416         
27417         if (this.video_url.length > 0) {
27418             img = {
27419                 tag : 'div',
27420                 cls : this.cls,
27421                 frameborder : 0,
27422                 allowfullscreen : true,
27423                 width : 420,  // these are for video tricks - that we replace the outer
27424                 height : 315,
27425                 src : this.video_url,
27426                 cn : [
27427                     img
27428                 ]
27429             };
27430         }
27431         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
27432         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
27433         
27434   
27435         var ret =   {
27436             tag: 'figure',
27437             'data-block' : 'Figure',
27438             'data-width' : this.width, 
27439             contenteditable : 'false',
27440             
27441             style : {
27442                 display: 'block',
27443                 float :  this.align ,
27444                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
27445                 width : this.align == 'center' ? '100%' : this.width,
27446                 margin:  '0px',
27447                 padding: this.align == 'center' ? '0' : '0 10px' ,
27448                 textAlign : this.align   // seems to work for email..
27449                 
27450             },
27451            
27452             
27453             align : this.align,
27454             cn : [
27455                 img,
27456               
27457                 {
27458                     tag: 'figcaption',
27459                     'data-display' : this.caption_display,
27460                     style : {
27461                         textAlign : 'left',
27462                         fontSize : '16px',
27463                         lineHeight : '24px',
27464                         display : this.caption_display,
27465                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
27466                         margin: m,
27467                         width: this.align == 'center' ?  this.width : '100%' 
27468                     
27469                          
27470                     },
27471                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
27472                     cn : [
27473                         {
27474                             tag: 'div',
27475                             style  : {
27476                                 marginTop : '16px',
27477                                 textAlign : 'left'
27478                             },
27479                             align: 'left',
27480                             cn : [
27481                                 {
27482                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
27483                                     tag : 'i',
27484                                     contenteditable : true,
27485                                     html : captionhtml
27486                                 }
27487                                 
27488                             ]
27489                         }
27490                         
27491                     ]
27492                     
27493                 }
27494             ]
27495         };
27496         return ret;
27497          
27498     },
27499     
27500     readElement : function(node)
27501     {
27502         // this should not really come from the link...
27503         this.video_url = this.getVal(node, 'div', 'src');
27504         this.cls = this.getVal(node, 'div', 'class');
27505         this.href = this.getVal(node, 'a', 'href');
27506         
27507         
27508         this.image_src = this.getVal(node, 'img', 'src');
27509          
27510         this.align = this.getVal(node, 'figure', 'align');
27511         var figcaption = this.getVal(node, 'figcaption', false);
27512         if (figcaption !== '') {
27513             this.caption = this.getVal(figcaption, 'i', 'html');
27514         }
27515         
27516
27517         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
27518         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
27519         this.width = this.getVal(node, true, 'data-width');
27520         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
27521         
27522     },
27523     removeNode : function()
27524     {
27525         return this.node;
27526     }
27527     
27528   
27529    
27530      
27531     
27532     
27533     
27534     
27535 })
27536
27537  
27538
27539 /**
27540  * @class Roo.htmleditor.BlockTable
27541  * Block that manages a table
27542  * 
27543  * @constructor
27544  * Create a new Filter.
27545  * @param {Object} config Configuration options
27546  */
27547
27548 Roo.htmleditor.BlockTable = function(cfg)
27549 {
27550     if (cfg.node) {
27551         this.readElement(cfg.node);
27552         this.updateElement(cfg.node);
27553     }
27554     Roo.apply(this, cfg);
27555     if (!cfg.node) {
27556         this.rows = [];
27557         for(var r = 0; r < this.no_row; r++) {
27558             this.rows[r] = [];
27559             for(var c = 0; c < this.no_col; c++) {
27560                 this.rows[r][c] = this.emptyCell();
27561             }
27562         }
27563     }
27564     
27565     
27566 }
27567 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
27568  
27569     rows : false,
27570     no_col : 1,
27571     no_row : 1,
27572     
27573     
27574     width: '100%',
27575     
27576     // used by context menu
27577     friendly_name : 'Table',
27578     deleteTitle : 'Delete Table',
27579     // context menu is drawn once..
27580     
27581     contextMenu : function(toolbar)
27582     {
27583         
27584         var block = function() {
27585             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
27586         };
27587         
27588         
27589         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
27590         
27591         var syncValue = toolbar.editorcore.syncValue;
27592         
27593         var fields = {};
27594         
27595         return [
27596             {
27597                 xtype : 'TextItem',
27598                 text : "Width: ",
27599                 xns : rooui.Toolbar  //Boostrap?
27600             },
27601             {
27602                 xtype : 'ComboBox',
27603                 allowBlank : false,
27604                 displayField : 'val',
27605                 editable : true,
27606                 listWidth : 100,
27607                 triggerAction : 'all',
27608                 typeAhead : true,
27609                 valueField : 'val',
27610                 width : 100,
27611                 name : 'width',
27612                 listeners : {
27613                     select : function (combo, r, index)
27614                     {
27615                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27616                         var b = block();
27617                         b.width = r.get('val');
27618                         b.updateElement();
27619                         syncValue();
27620                         toolbar.editorcore.onEditorEvent();
27621                     }
27622                 },
27623                 xns : rooui.form,
27624                 store : {
27625                     xtype : 'SimpleStore',
27626                     data : [
27627                         ['100%'],
27628                         ['auto']
27629                     ],
27630                     fields : [ 'val'],
27631                     xns : Roo.data
27632                 }
27633             },
27634             // -------- Cols
27635             
27636             {
27637                 xtype : 'TextItem',
27638                 text : "Columns: ",
27639                 xns : rooui.Toolbar  //Boostrap?
27640             },
27641          
27642             {
27643                 xtype : 'Button',
27644                 text: '-',
27645                 listeners : {
27646                     click : function (_self, e)
27647                     {
27648                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27649                         block().removeColumn();
27650                         syncValue();
27651                         toolbar.editorcore.onEditorEvent();
27652                     }
27653                 },
27654                 xns : rooui.Toolbar
27655             },
27656             {
27657                 xtype : 'Button',
27658                 text: '+',
27659                 listeners : {
27660                     click : function (_self, e)
27661                     {
27662                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27663                         block().addColumn();
27664                         syncValue();
27665                         toolbar.editorcore.onEditorEvent();
27666                     }
27667                 },
27668                 xns : rooui.Toolbar
27669             },
27670             // -------- ROWS
27671             {
27672                 xtype : 'TextItem',
27673                 text : "Rows: ",
27674                 xns : rooui.Toolbar  //Boostrap?
27675             },
27676          
27677             {
27678                 xtype : 'Button',
27679                 text: '-',
27680                 listeners : {
27681                     click : function (_self, e)
27682                     {
27683                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27684                         block().removeRow();
27685                         syncValue();
27686                         toolbar.editorcore.onEditorEvent();
27687                     }
27688                 },
27689                 xns : rooui.Toolbar
27690             },
27691             {
27692                 xtype : 'Button',
27693                 text: '+',
27694                 listeners : {
27695                     click : function (_self, e)
27696                     {
27697                         block().addRow();
27698                         syncValue();
27699                         toolbar.editorcore.onEditorEvent();
27700                     }
27701                 },
27702                 xns : rooui.Toolbar
27703             },
27704             // -------- ROWS
27705             {
27706                 xtype : 'Button',
27707                 text: 'Reset Column Widths',
27708                 listeners : {
27709                     
27710                     click : function (_self, e)
27711                     {
27712                         block().resetWidths();
27713                         syncValue();
27714                         toolbar.editorcore.onEditorEvent();
27715                     }
27716                 },
27717                 xns : rooui.Toolbar
27718             } 
27719             
27720             
27721             
27722         ];
27723         
27724     },
27725     
27726     
27727   /**
27728      * create a DomHelper friendly object - for use with
27729      * Roo.DomHelper.markup / overwrite / etc..
27730      * ?? should it be called with option to hide all editing features?
27731      */
27732     toObject : function()
27733     {
27734         
27735         var ret = {
27736             tag : 'table',
27737             contenteditable : 'false', // this stops cell selection from picking the table.
27738             'data-block' : 'Table',
27739             style : {
27740                 width:  this.width,
27741                 border : 'solid 1px #000', // ??? hard coded?
27742                 'border-collapse' : 'collapse' 
27743             },
27744             cn : [
27745                 { tag : 'tbody' , cn : [] }
27746             ]
27747         };
27748         
27749         // do we have a head = not really 
27750         var ncols = 0;
27751         Roo.each(this.rows, function( row ) {
27752             var tr = {
27753                 tag: 'tr',
27754                 style : {
27755                     margin: '6px',
27756                     border : 'solid 1px #000',
27757                     textAlign : 'left' 
27758                 },
27759                 cn : [ ]
27760             };
27761             
27762             ret.cn[0].cn.push(tr);
27763             // does the row have any properties? ?? height?
27764             var nc = 0;
27765             Roo.each(row, function( cell ) {
27766                 
27767                 var td = {
27768                     tag : 'td',
27769                     contenteditable :  'true',
27770                     'data-block' : 'Td',
27771                     html : cell.html,
27772                     style : cell.style
27773                 };
27774                 if (cell.colspan > 1) {
27775                     td.colspan = cell.colspan ;
27776                     nc += cell.colspan;
27777                 } else {
27778                     nc++;
27779                 }
27780                 if (cell.rowspan > 1) {
27781                     td.rowspan = cell.rowspan ;
27782                 }
27783                 
27784                 
27785                 // widths ?
27786                 tr.cn.push(td);
27787                     
27788                 
27789             }, this);
27790             ncols = Math.max(nc, ncols);
27791             
27792             
27793         }, this);
27794         // add the header row..
27795         
27796         ncols++;
27797          
27798         
27799         return ret;
27800          
27801     },
27802     
27803     readElement : function(node)
27804     {
27805         node  = node ? node : this.node ;
27806         this.width = this.getVal(node, true, 'style', 'width') || '100%';
27807         
27808         this.rows = [];
27809         this.no_row = 0;
27810         var trs = Array.from(node.rows);
27811         trs.forEach(function(tr) {
27812             var row =  [];
27813             this.rows.push(row);
27814             
27815             this.no_row++;
27816             var no_column = 0;
27817             Array.from(tr.cells).forEach(function(td) {
27818                 
27819                 var add = {
27820                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
27821                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
27822                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
27823                     html : td.innerHTML
27824                 };
27825                 no_column += add.colspan;
27826                      
27827                 
27828                 row.push(add);
27829                 
27830                 
27831             },this);
27832             this.no_col = Math.max(this.no_col, no_column);
27833             
27834             
27835         },this);
27836         
27837         
27838     },
27839     normalizeRows: function()
27840     {
27841         var ret= [];
27842         var rid = -1;
27843         this.rows.forEach(function(row) {
27844             rid++;
27845             ret[rid] = [];
27846             row = this.normalizeRow(row);
27847             var cid = 0;
27848             row.forEach(function(c) {
27849                 while (typeof(ret[rid][cid]) != 'undefined') {
27850                     cid++;
27851                 }
27852                 if (typeof(ret[rid]) == 'undefined') {
27853                     ret[rid] = [];
27854                 }
27855                 ret[rid][cid] = c;
27856                 c.row = rid;
27857                 c.col = cid;
27858                 if (c.rowspan < 2) {
27859                     return;
27860                 }
27861                 
27862                 for(var i = 1 ;i < c.rowspan; i++) {
27863                     if (typeof(ret[rid+i]) == 'undefined') {
27864                         ret[rid+i] = [];
27865                     }
27866                     ret[rid+i][cid] = c;
27867                 }
27868             });
27869         }, this);
27870         return ret;
27871     
27872     },
27873     
27874     normalizeRow: function(row)
27875     {
27876         var ret= [];
27877         row.forEach(function(c) {
27878             if (c.colspan < 2) {
27879                 ret.push(c);
27880                 return;
27881             }
27882             for(var i =0 ;i < c.colspan; i++) {
27883                 ret.push(c);
27884             }
27885         });
27886         return ret;
27887     
27888     },
27889     
27890     deleteColumn : function(sel)
27891     {
27892         if (!sel || sel.type != 'col') {
27893             return;
27894         }
27895         if (this.no_col < 2) {
27896             return;
27897         }
27898         
27899         this.rows.forEach(function(row) {
27900             var cols = this.normalizeRow(row);
27901             var col = cols[sel.col];
27902             if (col.colspan > 1) {
27903                 col.colspan --;
27904             } else {
27905                 row.remove(col);
27906             }
27907             
27908         }, this);
27909         this.no_col--;
27910         
27911     },
27912     removeColumn : function()
27913     {
27914         this.deleteColumn({
27915             type: 'col',
27916             col : this.no_col-1
27917         });
27918         this.updateElement();
27919     },
27920     
27921      
27922     addColumn : function()
27923     {
27924         
27925         this.rows.forEach(function(row) {
27926             row.push(this.emptyCell());
27927            
27928         }, this);
27929         this.updateElement();
27930     },
27931     
27932     deleteRow : function(sel)
27933     {
27934         if (!sel || sel.type != 'row') {
27935             return;
27936         }
27937         
27938         if (this.no_row < 2) {
27939             return;
27940         }
27941         
27942         var rows = this.normalizeRows();
27943         
27944         
27945         rows[sel.row].forEach(function(col) {
27946             if (col.rowspan > 1) {
27947                 col.rowspan--;
27948             } else {
27949                 col.remove = 1; // flage it as removed.
27950             }
27951             
27952         }, this);
27953         var newrows = [];
27954         this.rows.forEach(function(row) {
27955             newrow = [];
27956             row.forEach(function(c) {
27957                 if (typeof(c.remove) == 'undefined') {
27958                     newrow.push(c);
27959                 }
27960                 
27961             });
27962             if (newrow.length > 0) {
27963                 newrows.push(row);
27964             }
27965         });
27966         this.rows =  newrows;
27967         
27968         
27969         
27970         this.no_row--;
27971         this.updateElement();
27972         
27973     },
27974     removeRow : function()
27975     {
27976         this.deleteRow({
27977             type: 'row',
27978             row : this.no_row-1
27979         });
27980         
27981     },
27982     
27983      
27984     addRow : function()
27985     {
27986         
27987         var row = [];
27988         for (var i = 0; i < this.no_col; i++ ) {
27989             
27990             row.push(this.emptyCell());
27991            
27992         }
27993         this.rows.push(row);
27994         this.updateElement();
27995         
27996     },
27997      
27998     // the default cell object... at present...
27999     emptyCell : function() {
28000         return (new Roo.htmleditor.BlockTd({})).toObject();
28001         
28002      
28003     },
28004     
28005     removeNode : function()
28006     {
28007         return this.node;
28008     },
28009     
28010     
28011     
28012     resetWidths : function()
28013     {
28014         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
28015             var nn = Roo.htmleditor.Block.factory(n);
28016             nn.width = '';
28017             nn.updateElement(n);
28018         });
28019     }
28020     
28021     
28022     
28023     
28024 })
28025
28026 /**
28027  *
28028  * editing a TD?
28029  *
28030  * since selections really work on the table cell, then editing really should work from there
28031  *
28032  * The original plan was to support merging etc... - but that may not be needed yet..
28033  *
28034  * So this simple version will support:
28035  *   add/remove cols
28036  *   adjust the width +/-
28037  *   reset the width...
28038  *   
28039  *
28040  */
28041
28042
28043  
28044
28045 /**
28046  * @class Roo.htmleditor.BlockTable
28047  * Block that manages a table
28048  * 
28049  * @constructor
28050  * Create a new Filter.
28051  * @param {Object} config Configuration options
28052  */
28053
28054 Roo.htmleditor.BlockTd = function(cfg)
28055 {
28056     if (cfg.node) {
28057         this.readElement(cfg.node);
28058         this.updateElement(cfg.node);
28059     }
28060     Roo.apply(this, cfg);
28061      
28062     
28063     
28064 }
28065 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
28066  
28067     node : false,
28068     
28069     width: '',
28070     textAlign : 'left',
28071     valign : 'top',
28072     
28073     colspan : 1,
28074     rowspan : 1,
28075     
28076     
28077     // used by context menu
28078     friendly_name : 'Table Cell',
28079     deleteTitle : false, // use our customer delete
28080     
28081     // context menu is drawn once..
28082     
28083     contextMenu : function(toolbar)
28084     {
28085         
28086         var cell = function() {
28087             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
28088         };
28089         
28090         var table = function() {
28091             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
28092         };
28093         
28094         var lr = false;
28095         var saveSel = function()
28096         {
28097             lr = toolbar.editorcore.getSelection().getRangeAt(0);
28098         }
28099         var restoreSel = function()
28100         {
28101             if (lr) {
28102                 (function() {
28103                     toolbar.editorcore.focus();
28104                     var cr = toolbar.editorcore.getSelection();
28105                     cr.removeAllRanges();
28106                     cr.addRange(lr);
28107                     toolbar.editorcore.onEditorEvent();
28108                 }).defer(10, this);
28109                 
28110                 
28111             }
28112         }
28113         
28114         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
28115         
28116         var syncValue = toolbar.editorcore.syncValue;
28117         
28118         var fields = {};
28119         
28120         return [
28121             {
28122                 xtype : 'Button',
28123                 text : 'Edit Table',
28124                 listeners : {
28125                     click : function() {
28126                         var t = toolbar.tb.selectedNode.closest('table');
28127                         toolbar.editorcore.selectNode(t);
28128                         toolbar.editorcore.onEditorEvent();                        
28129                     }
28130                 }
28131                 
28132             },
28133               
28134            
28135              
28136             {
28137                 xtype : 'TextItem',
28138                 text : "Column Width: ",
28139                  xns : rooui.Toolbar 
28140                
28141             },
28142             {
28143                 xtype : 'Button',
28144                 text: '-',
28145                 listeners : {
28146                     click : function (_self, e)
28147                     {
28148                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28149                         cell().shrinkColumn();
28150                         syncValue();
28151                          toolbar.editorcore.onEditorEvent();
28152                     }
28153                 },
28154                 xns : rooui.Toolbar
28155             },
28156             {
28157                 xtype : 'Button',
28158                 text: '+',
28159                 listeners : {
28160                     click : function (_self, e)
28161                     {
28162                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28163                         cell().growColumn();
28164                         syncValue();
28165                         toolbar.editorcore.onEditorEvent();
28166                     }
28167                 },
28168                 xns : rooui.Toolbar
28169             },
28170             
28171             {
28172                 xtype : 'TextItem',
28173                 text : "Vertical Align: ",
28174                 xns : rooui.Toolbar  //Boostrap?
28175             },
28176             {
28177                 xtype : 'ComboBox',
28178                 allowBlank : false,
28179                 displayField : 'val',
28180                 editable : true,
28181                 listWidth : 100,
28182                 triggerAction : 'all',
28183                 typeAhead : true,
28184                 valueField : 'val',
28185                 width : 100,
28186                 name : 'valign',
28187                 listeners : {
28188                     select : function (combo, r, index)
28189                     {
28190                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28191                         var b = cell();
28192                         b.valign = r.get('val');
28193                         b.updateElement();
28194                         syncValue();
28195                         toolbar.editorcore.onEditorEvent();
28196                     }
28197                 },
28198                 xns : rooui.form,
28199                 store : {
28200                     xtype : 'SimpleStore',
28201                     data : [
28202                         ['top'],
28203                         ['middle'],
28204                         ['bottom'] // there are afew more... 
28205                     ],
28206                     fields : [ 'val'],
28207                     xns : Roo.data
28208                 }
28209             },
28210             
28211             {
28212                 xtype : 'TextItem',
28213                 text : "Merge Cells: ",
28214                  xns : rooui.Toolbar 
28215                
28216             },
28217             
28218             
28219             {
28220                 xtype : 'Button',
28221                 text: 'Right',
28222                 listeners : {
28223                     click : function (_self, e)
28224                     {
28225                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28226                         cell().mergeRight();
28227                         //block().growColumn();
28228                         syncValue();
28229                         toolbar.editorcore.onEditorEvent();
28230                     }
28231                 },
28232                 xns : rooui.Toolbar
28233             },
28234              
28235             {
28236                 xtype : 'Button',
28237                 text: 'Below',
28238                 listeners : {
28239                     click : function (_self, e)
28240                     {
28241                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28242                         cell().mergeBelow();
28243                         //block().growColumn();
28244                         syncValue();
28245                         toolbar.editorcore.onEditorEvent();
28246                     }
28247                 },
28248                 xns : rooui.Toolbar
28249             },
28250             {
28251                 xtype : 'TextItem',
28252                 text : "| ",
28253                  xns : rooui.Toolbar 
28254                
28255             },
28256             
28257             {
28258                 xtype : 'Button',
28259                 text: 'Split',
28260                 listeners : {
28261                     click : function (_self, e)
28262                     {
28263                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28264                         cell().split();
28265                         syncValue();
28266                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28267                         toolbar.editorcore.onEditorEvent();
28268                                              
28269                     }
28270                 },
28271                 xns : rooui.Toolbar
28272             },
28273             {
28274                 xtype : 'Fill',
28275                 xns : rooui.Toolbar 
28276                
28277             },
28278         
28279           
28280             {
28281                 xtype : 'Button',
28282                 text: 'Delete',
28283                  
28284                 xns : rooui.Toolbar,
28285                 menu : {
28286                     xtype : 'Menu',
28287                     xns : rooui.menu,
28288                     items : [
28289                         {
28290                             xtype : 'Item',
28291                             html: 'Column',
28292                             listeners : {
28293                                 click : function (_self, e)
28294                                 {
28295                                     var t = table();
28296                                     
28297                                     cell().deleteColumn();
28298                                     syncValue();
28299                                     toolbar.editorcore.selectNode(t.node);
28300                                     toolbar.editorcore.onEditorEvent();   
28301                                 }
28302                             },
28303                             xns : rooui.menu
28304                         },
28305                         {
28306                             xtype : 'Item',
28307                             html: 'Row',
28308                             listeners : {
28309                                 click : function (_self, e)
28310                                 {
28311                                     var t = table();
28312                                     cell().deleteRow();
28313                                     syncValue();
28314                                     
28315                                     toolbar.editorcore.selectNode(t.node);
28316                                     toolbar.editorcore.onEditorEvent();   
28317                                                          
28318                                 }
28319                             },
28320                             xns : rooui.menu
28321                         },
28322                        {
28323                             xtype : 'Separator',
28324                             xns : rooui.menu
28325                         },
28326                         {
28327                             xtype : 'Item',
28328                             html: 'Table',
28329                             listeners : {
28330                                 click : function (_self, e)
28331                                 {
28332                                     var t = table();
28333                                     var nn = t.node.nextSibling || t.node.previousSibling;
28334                                     t.node.parentNode.removeChild(t.node);
28335                                     if (nn) { 
28336                                         toolbar.editorcore.selectNode(nn, true);
28337                                     }
28338                                     toolbar.editorcore.onEditorEvent();   
28339                                                          
28340                                 }
28341                             },
28342                             xns : rooui.menu
28343                         }
28344                     ]
28345                 }
28346             }
28347             
28348             // align... << fixme
28349             
28350         ];
28351         
28352     },
28353     
28354     
28355   /**
28356      * create a DomHelper friendly object - for use with
28357      * Roo.DomHelper.markup / overwrite / etc..
28358      * ?? should it be called with option to hide all editing features?
28359      */
28360  /**
28361      * create a DomHelper friendly object - for use with
28362      * Roo.DomHelper.markup / overwrite / etc..
28363      * ?? should it be called with option to hide all editing features?
28364      */
28365     toObject : function()
28366     {
28367         
28368         var ret = {
28369             tag : 'td',
28370             contenteditable : 'true', // this stops cell selection from picking the table.
28371             'data-block' : 'Td',
28372             valign : this.valign,
28373             style : {  
28374                 'text-align' :  this.textAlign,
28375                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
28376                 'border-collapse' : 'collapse',
28377                 padding : '6px', // 8 for desktop / 4 for mobile
28378                 'vertical-align': this.valign
28379             },
28380             html : this.html
28381         };
28382         if (this.width != '') {
28383             ret.width = this.width;
28384             ret.style.width = this.width;
28385         }
28386         
28387         
28388         if (this.colspan > 1) {
28389             ret.colspan = this.colspan ;
28390         } 
28391         if (this.rowspan > 1) {
28392             ret.rowspan = this.rowspan ;
28393         }
28394         
28395            
28396         
28397         return ret;
28398          
28399     },
28400     
28401     readElement : function(node)
28402     {
28403         node  = node ? node : this.node ;
28404         this.width = node.style.width;
28405         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
28406         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
28407         this.html = node.innerHTML;
28408         
28409         
28410     },
28411      
28412     // the default cell object... at present...
28413     emptyCell : function() {
28414         return {
28415             colspan :  1,
28416             rowspan :  1,
28417             textAlign : 'left',
28418             html : "&nbsp;" // is this going to be editable now?
28419         };
28420      
28421     },
28422     
28423     removeNode : function()
28424     {
28425         return this.node.closest('table');
28426          
28427     },
28428     
28429     cellData : false,
28430     
28431     colWidths : false,
28432     
28433     toTableArray  : function()
28434     {
28435         var ret = [];
28436         var tab = this.node.closest('tr').closest('table');
28437         Array.from(tab.rows).forEach(function(r, ri){
28438             ret[ri] = [];
28439         });
28440         var rn = 0;
28441         this.colWidths = [];
28442         var all_auto = true;
28443         Array.from(tab.rows).forEach(function(r, ri){
28444             
28445             var cn = 0;
28446             Array.from(r.cells).forEach(function(ce, ci){
28447                 var c =  {
28448                     cell : ce,
28449                     row : rn,
28450                     col: cn,
28451                     colspan : ce.colSpan,
28452                     rowspan : ce.rowSpan
28453                 };
28454                 if (ce.isEqualNode(this.node)) {
28455                     this.cellData = c;
28456                 }
28457                 // if we have been filled up by a row?
28458                 if (typeof(ret[rn][cn]) != 'undefined') {
28459                     while(typeof(ret[rn][cn]) != 'undefined') {
28460                         cn++;
28461                     }
28462                     c.col = cn;
28463                 }
28464                 
28465                 if (typeof(this.colWidths[cn]) == 'undefined') {
28466                     this.colWidths[cn] =   ce.style.width;
28467                     if (this.colWidths[cn] != '') {
28468                         all_auto = false;
28469                     }
28470                 }
28471                 
28472                 
28473                 if (c.colspan < 2 && c.rowspan < 2 ) {
28474                     ret[rn][cn] = c;
28475                     cn++;
28476                     return;
28477                 }
28478                 for(var j = 0; j < c.rowspan; j++) {
28479                     if (typeof(ret[rn+j]) == 'undefined') {
28480                         continue; // we have a problem..
28481                     }
28482                     ret[rn+j][cn] = c;
28483                     for(var i = 0; i < c.colspan; i++) {
28484                         ret[rn+j][cn+i] = c;
28485                     }
28486                 }
28487                 
28488                 cn += c.colspan;
28489             }, this);
28490             rn++;
28491         }, this);
28492         
28493         // initalize widths.?
28494         // either all widths or no widths..
28495         if (all_auto) {
28496             this.colWidths[0] = false; // no widths flag.
28497         }
28498         
28499         
28500         return ret;
28501         
28502     },
28503     
28504     
28505     
28506     
28507     mergeRight: function()
28508     {
28509          
28510         // get the contents of the next cell along..
28511         var tr = this.node.closest('tr');
28512         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
28513         if (i >= tr.childNodes.length - 1) {
28514             return; // no cells on right to merge with.
28515         }
28516         var table = this.toTableArray();
28517         
28518         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
28519             return; // nothing right?
28520         }
28521         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
28522         // right cell - must be same rowspan and on the same row.
28523         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
28524             return; // right hand side is not same rowspan.
28525         }
28526         
28527         
28528         
28529         this.node.innerHTML += ' ' + rc.cell.innerHTML;
28530         tr.removeChild(rc.cell);
28531         this.colspan += rc.colspan;
28532         this.node.setAttribute('colspan', this.colspan);
28533
28534     },
28535     
28536     
28537     mergeBelow : function()
28538     {
28539         var table = this.toTableArray();
28540         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
28541             return; // no row below
28542         }
28543         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
28544             return; // nothing right?
28545         }
28546         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
28547         
28548         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
28549             return; // right hand side is not same rowspan.
28550         }
28551         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
28552         rc.cell.parentNode.removeChild(rc.cell);
28553         this.rowspan += rc.rowspan;
28554         this.node.setAttribute('rowspan', this.rowspan);
28555     },
28556     
28557     split: function()
28558     {
28559         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
28560             return;
28561         }
28562         var table = this.toTableArray();
28563         var cd = this.cellData;
28564         this.rowspan = 1;
28565         this.colspan = 1;
28566         
28567         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
28568             
28569             
28570             
28571             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
28572                 if (r == cd.row && c == cd.col) {
28573                     this.node.removeAttribute('rowspan');
28574                     this.node.removeAttribute('colspan');
28575                     continue;
28576                 }
28577                  
28578                 var ntd = this.node.cloneNode(); // which col/row should be 0..
28579                 ntd.removeAttribute('id'); //
28580                 //ntd.style.width  = '';
28581                 ntd.innerHTML = '';
28582                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
28583             }
28584             
28585         }
28586         this.redrawAllCells(table);
28587         
28588          
28589         
28590     },
28591     
28592     
28593     
28594     redrawAllCells: function(table)
28595     {
28596         
28597          
28598         var tab = this.node.closest('tr').closest('table');
28599         var ctr = tab.rows[0].parentNode;
28600         Array.from(tab.rows).forEach(function(r, ri){
28601             
28602             Array.from(r.cells).forEach(function(ce, ci){
28603                 ce.parentNode.removeChild(ce);
28604             });
28605             r.parentNode.removeChild(r);
28606         });
28607         for(var r = 0 ; r < table.length; r++) {
28608             var re = tab.rows[r];
28609             
28610             var re = tab.ownerDocument.createElement('tr');
28611             ctr.appendChild(re);
28612             for(var c = 0 ; c < table[r].length; c++) {
28613                 if (table[r][c].cell === false) {
28614                     continue;
28615                 }
28616                 
28617                 re.appendChild(table[r][c].cell);
28618                  
28619                 table[r][c].cell = false;
28620             }
28621         }
28622         
28623     },
28624     updateWidths : function(table)
28625     {
28626         for(var r = 0 ; r < table.length; r++) {
28627            
28628             for(var c = 0 ; c < table[r].length; c++) {
28629                 if (table[r][c].cell === false) {
28630                     continue;
28631                 }
28632                 
28633                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
28634                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
28635                     el.width = Math.floor(this.colWidths[c])  +'%';
28636                     el.updateElement(el.node);
28637                 }
28638                 table[r][c].cell = false; // done
28639             }
28640         }
28641     },
28642     normalizeWidths : function(table)
28643     {
28644     
28645         if (this.colWidths[0] === false) {
28646             var nw = 100.0 / this.colWidths.length;
28647             this.colWidths.forEach(function(w,i) {
28648                 this.colWidths[i] = nw;
28649             },this);
28650             return;
28651         }
28652     
28653         var t = 0, missing = [];
28654         
28655         this.colWidths.forEach(function(w,i) {
28656             //if you mix % and
28657             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
28658             var add =  this.colWidths[i];
28659             if (add > 0) {
28660                 t+=add;
28661                 return;
28662             }
28663             missing.push(i);
28664             
28665             
28666         },this);
28667         var nc = this.colWidths.length;
28668         if (missing.length) {
28669             var mult = (nc - missing.length) / (1.0 * nc);
28670             var t = mult * t;
28671             var ew = (100 -t) / (1.0 * missing.length);
28672             this.colWidths.forEach(function(w,i) {
28673                 if (w > 0) {
28674                     this.colWidths[i] = w * mult;
28675                     return;
28676                 }
28677                 
28678                 this.colWidths[i] = ew;
28679             }, this);
28680             // have to make up numbers..
28681              
28682         }
28683         // now we should have all the widths..
28684         
28685     
28686     },
28687     
28688     shrinkColumn : function()
28689     {
28690         var table = this.toTableArray();
28691         this.normalizeWidths(table);
28692         var col = this.cellData.col;
28693         var nw = this.colWidths[col] * 0.8;
28694         if (nw < 5) {
28695             return;
28696         }
28697         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
28698         this.colWidths.forEach(function(w,i) {
28699             if (i == col) {
28700                  this.colWidths[i] = nw;
28701                 return;
28702             }
28703             this.colWidths[i] += otherAdd
28704         }, this);
28705         this.updateWidths(table);
28706          
28707     },
28708     growColumn : function()
28709     {
28710         var table = this.toTableArray();
28711         this.normalizeWidths(table);
28712         var col = this.cellData.col;
28713         var nw = this.colWidths[col] * 1.2;
28714         if (nw > 90) {
28715             return;
28716         }
28717         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
28718         this.colWidths.forEach(function(w,i) {
28719             if (i == col) {
28720                 this.colWidths[i] = nw;
28721                 return;
28722             }
28723             this.colWidths[i] -= otherSub
28724         }, this);
28725         this.updateWidths(table);
28726          
28727     },
28728     deleteRow : function()
28729     {
28730         // delete this rows 'tr'
28731         // if any of the cells in this row have a rowspan > 1 && row!= this row..
28732         // then reduce the rowspan.
28733         var table = this.toTableArray();
28734         // this.cellData.row;
28735         for (var i =0;i< table[this.cellData.row].length ; i++) {
28736             var c = table[this.cellData.row][i];
28737             if (c.row != this.cellData.row) {
28738                 
28739                 c.rowspan--;
28740                 c.cell.setAttribute('rowspan', c.rowspan);
28741                 continue;
28742             }
28743             if (c.rowspan > 1) {
28744                 c.rowspan--;
28745                 c.cell.setAttribute('rowspan', c.rowspan);
28746             }
28747         }
28748         table.splice(this.cellData.row,1);
28749         this.redrawAllCells(table);
28750         
28751     },
28752     deleteColumn : function()
28753     {
28754         var table = this.toTableArray();
28755         
28756         for (var i =0;i< table.length ; i++) {
28757             var c = table[i][this.cellData.col];
28758             if (c.col != this.cellData.col) {
28759                 table[i][this.cellData.col].colspan--;
28760             } else if (c.colspan > 1) {
28761                 c.colspan--;
28762                 c.cell.setAttribute('colspan', c.colspan);
28763             }
28764             table[i].splice(this.cellData.col,1);
28765         }
28766         
28767         this.redrawAllCells(table);
28768     }
28769     
28770     
28771     
28772     
28773 })
28774
28775 //<script type="text/javascript">
28776
28777 /*
28778  * Based  Ext JS Library 1.1.1
28779  * Copyright(c) 2006-2007, Ext JS, LLC.
28780  * LGPL
28781  *
28782  */
28783  
28784 /**
28785  * @class Roo.HtmlEditorCore
28786  * @extends Roo.Component
28787  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
28788  *
28789  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
28790  */
28791
28792 Roo.HtmlEditorCore = function(config){
28793     
28794     
28795     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
28796     
28797     
28798     this.addEvents({
28799         /**
28800          * @event initialize
28801          * Fires when the editor is fully initialized (including the iframe)
28802          * @param {Roo.HtmlEditorCore} this
28803          */
28804         initialize: true,
28805         /**
28806          * @event activate
28807          * Fires when the editor is first receives the focus. Any insertion must wait
28808          * until after this event.
28809          * @param {Roo.HtmlEditorCore} this
28810          */
28811         activate: true,
28812          /**
28813          * @event beforesync
28814          * Fires before the textarea is updated with content from the editor iframe. Return false
28815          * to cancel the sync.
28816          * @param {Roo.HtmlEditorCore} this
28817          * @param {String} html
28818          */
28819         beforesync: true,
28820          /**
28821          * @event beforepush
28822          * Fires before the iframe editor is updated with content from the textarea. Return false
28823          * to cancel the push.
28824          * @param {Roo.HtmlEditorCore} this
28825          * @param {String} html
28826          */
28827         beforepush: true,
28828          /**
28829          * @event sync
28830          * Fires when the textarea is updated with content from the editor iframe.
28831          * @param {Roo.HtmlEditorCore} this
28832          * @param {String} html
28833          */
28834         sync: true,
28835          /**
28836          * @event push
28837          * Fires when the iframe editor is updated with content from the textarea.
28838          * @param {Roo.HtmlEditorCore} this
28839          * @param {String} html
28840          */
28841         push: true,
28842         
28843         /**
28844          * @event editorevent
28845          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
28846          * @param {Roo.HtmlEditorCore} this
28847          */
28848         editorevent: true 
28849          
28850         
28851     });
28852     
28853     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
28854     
28855     // defaults : white / black...
28856     this.applyBlacklists();
28857     
28858     
28859     
28860 };
28861
28862
28863 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
28864
28865
28866      /**
28867      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
28868      */
28869     
28870     owner : false,
28871     
28872      /**
28873      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
28874      *                        Roo.resizable.
28875      */
28876     resizable : false,
28877      /**
28878      * @cfg {Number} height (in pixels)
28879      */   
28880     height: 300,
28881    /**
28882      * @cfg {Number} width (in pixels)
28883      */   
28884     width: 500,
28885      /**
28886      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
28887      *         if you are doing an email editor, this probably needs disabling, it's designed
28888      */
28889     autoClean: true,
28890     
28891     /**
28892      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
28893      */
28894     enableBlocks : true,
28895     /**
28896      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
28897      * 
28898      */
28899     stylesheets: false,
28900      /**
28901      * @cfg {String} language default en - language of text (usefull for rtl languages)
28902      * 
28903      */
28904     language: 'en',
28905     
28906     /**
28907      * @cfg {boolean} allowComments - default false - allow comments in HTML source
28908      *          - by default they are stripped - if you are editing email you may need this.
28909      */
28910     allowComments: false,
28911     // id of frame..
28912     frameId: false,
28913     
28914     // private properties
28915     validationEvent : false,
28916     deferHeight: true,
28917     initialized : false,
28918     activated : false,
28919     sourceEditMode : false,
28920     onFocus : Roo.emptyFn,
28921     iframePad:3,
28922     hideMode:'offsets',
28923     
28924     clearUp: true,
28925     
28926     // blacklist + whitelisted elements..
28927     black: false,
28928     white: false,
28929      
28930     bodyCls : '',
28931
28932     
28933     undoManager : false,
28934     /**
28935      * Protected method that will not generally be called directly. It
28936      * is called when the editor initializes the iframe with HTML contents. Override this method if you
28937      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
28938      */
28939     getDocMarkup : function(){
28940         // body styles..
28941         var st = '';
28942         
28943         // inherit styels from page...?? 
28944         if (this.stylesheets === false) {
28945             
28946             Roo.get(document.head).select('style').each(function(node) {
28947                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
28948             });
28949             
28950             Roo.get(document.head).select('link').each(function(node) { 
28951                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
28952             });
28953             
28954         } else if (!this.stylesheets.length) {
28955                 // simple..
28956                 st = '<style type="text/css">' +
28957                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
28958                    '</style>';
28959         } else {
28960             for (var i in this.stylesheets) {
28961                 if (typeof(this.stylesheets[i]) != 'string') {
28962                     continue;
28963                 }
28964                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
28965             }
28966             
28967         }
28968         
28969         st +=  '<style type="text/css">' +
28970             'IMG { cursor: pointer } ' +
28971         '</style>';
28972         
28973         st += '<meta name="google" content="notranslate">';
28974         
28975         var cls = 'notranslate roo-htmleditor-body';
28976         
28977         if(this.bodyCls.length){
28978             cls += ' ' + this.bodyCls;
28979         }
28980         
28981         return '<html  class="notranslate" translate="no"><head>' + st  +
28982             //<style type="text/css">' +
28983             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
28984             //'</style>' +
28985             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
28986     },
28987
28988     // private
28989     onRender : function(ct, position)
28990     {
28991         var _t = this;
28992         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
28993         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
28994         
28995         
28996         this.el.dom.style.border = '0 none';
28997         this.el.dom.setAttribute('tabIndex', -1);
28998         this.el.addClass('x-hidden hide');
28999         
29000         
29001         
29002         if(Roo.isIE){ // fix IE 1px bogus margin
29003             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
29004         }
29005        
29006         
29007         this.frameId = Roo.id();
29008         
29009          
29010         
29011         var iframe = this.owner.wrap.createChild({
29012             tag: 'iframe',
29013             cls: 'form-control', // bootstrap..
29014             id: this.frameId,
29015             name: this.frameId,
29016             frameBorder : 'no',
29017             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
29018         }, this.el
29019         );
29020         
29021         
29022         this.iframe = iframe.dom;
29023
29024         this.assignDocWin();
29025         
29026         this.doc.designMode = 'on';
29027        
29028         this.doc.open();
29029         this.doc.write(this.getDocMarkup());
29030         this.doc.close();
29031
29032         
29033         var task = { // must defer to wait for browser to be ready
29034             run : function(){
29035                 //console.log("run task?" + this.doc.readyState);
29036                 this.assignDocWin();
29037                 if(this.doc.body || this.doc.readyState == 'complete'){
29038                     try {
29039                         this.doc.designMode="on";
29040                         
29041                     } catch (e) {
29042                         return;
29043                     }
29044                     Roo.TaskMgr.stop(task);
29045                     this.initEditor.defer(10, this);
29046                 }
29047             },
29048             interval : 10,
29049             duration: 10000,
29050             scope: this
29051         };
29052         Roo.TaskMgr.start(task);
29053
29054     },
29055
29056     // private
29057     onResize : function(w, h)
29058     {
29059          Roo.log('resize: ' +w + ',' + h );
29060         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
29061         if(!this.iframe){
29062             return;
29063         }
29064         if(typeof w == 'number'){
29065             
29066             this.iframe.style.width = w + 'px';
29067         }
29068         if(typeof h == 'number'){
29069             
29070             this.iframe.style.height = h + 'px';
29071             if(this.doc){
29072                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
29073             }
29074         }
29075         
29076     },
29077
29078     /**
29079      * Toggles the editor between standard and source edit mode.
29080      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
29081      */
29082     toggleSourceEdit : function(sourceEditMode){
29083         
29084         this.sourceEditMode = sourceEditMode === true;
29085         
29086         if(this.sourceEditMode){
29087  
29088             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
29089             
29090         }else{
29091             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
29092             //this.iframe.className = '';
29093             this.deferFocus();
29094         }
29095         //this.setSize(this.owner.wrap.getSize());
29096         //this.fireEvent('editmodechange', this, this.sourceEditMode);
29097     },
29098
29099     
29100   
29101
29102     /**
29103      * Protected method that will not generally be called directly. If you need/want
29104      * custom HTML cleanup, this is the method you should override.
29105      * @param {String} html The HTML to be cleaned
29106      * return {String} The cleaned HTML
29107      */
29108     cleanHtml : function(html)
29109     {
29110         html = String(html);
29111         if(html.length > 5){
29112             if(Roo.isSafari){ // strip safari nonsense
29113                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
29114             }
29115         }
29116         if(html == '&nbsp;'){
29117             html = '';
29118         }
29119         return html;
29120     },
29121
29122     /**
29123      * HTML Editor -> Textarea
29124      * Protected method that will not generally be called directly. Syncs the contents
29125      * of the editor iframe with the textarea.
29126      */
29127     syncValue : function()
29128     {
29129         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
29130         if(this.initialized){
29131             
29132             if (this.undoManager) {
29133                 this.undoManager.addEvent();
29134             }
29135
29136             
29137             var bd = (this.doc.body || this.doc.documentElement);
29138            
29139             
29140             var sel = this.win.getSelection();
29141             
29142             var div = document.createElement('div');
29143             div.innerHTML = bd.innerHTML;
29144             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
29145             if (gtx.length > 0) {
29146                 var rm = gtx.item(0).parentNode;
29147                 rm.parentNode.removeChild(rm);
29148             }
29149             
29150            
29151             if (this.enableBlocks) {
29152                 new Roo.htmleditor.FilterBlock({ node : div });
29153             }
29154             //?? tidy?
29155             var tidy = new Roo.htmleditor.TidySerializer({
29156                 inner:  true
29157             });
29158             var html  = tidy.serialize(div);
29159             
29160             
29161             if(Roo.isSafari){
29162                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
29163                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
29164                 if(m && m[1]){
29165                     html = '<div style="'+m[0]+'">' + html + '</div>';
29166                 }
29167             }
29168             html = this.cleanHtml(html);
29169             // fix up the special chars.. normaly like back quotes in word...
29170             // however we do not want to do this with chinese..
29171             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
29172                 
29173                 var cc = match.charCodeAt();
29174
29175                 // Get the character value, handling surrogate pairs
29176                 if (match.length == 2) {
29177                     // It's a surrogate pair, calculate the Unicode code point
29178                     var high = match.charCodeAt(0) - 0xD800;
29179                     var low  = match.charCodeAt(1) - 0xDC00;
29180                     cc = (high * 0x400) + low + 0x10000;
29181                 }  else if (
29182                     (cc >= 0x4E00 && cc < 0xA000 ) ||
29183                     (cc >= 0x3400 && cc < 0x4E00 ) ||
29184                     (cc >= 0xf900 && cc < 0xfb00 )
29185                 ) {
29186                         return match;
29187                 }  
29188          
29189                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
29190                 return "&#" + cc + ";";
29191                 
29192                 
29193             });
29194             
29195             
29196              
29197             if(this.owner.fireEvent('beforesync', this, html) !== false){
29198                 this.el.dom.value = html;
29199                 this.owner.fireEvent('sync', this, html);
29200             }
29201         }
29202     },
29203
29204     /**
29205      * TEXTAREA -> EDITABLE
29206      * Protected method that will not generally be called directly. Pushes the value of the textarea
29207      * into the iframe editor.
29208      */
29209     pushValue : function()
29210     {
29211         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
29212         if(this.initialized){
29213             var v = this.el.dom.value.trim();
29214             
29215             
29216             if(this.owner.fireEvent('beforepush', this, v) !== false){
29217                 var d = (this.doc.body || this.doc.documentElement);
29218                 d.innerHTML = v;
29219                  
29220                 this.el.dom.value = d.innerHTML;
29221                 this.owner.fireEvent('push', this, v);
29222             }
29223             if (this.autoClean) {
29224                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
29225                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
29226             }
29227             if (this.enableBlocks) {
29228                 Roo.htmleditor.Block.initAll(this.doc.body);
29229             }
29230             
29231             this.updateLanguage();
29232             
29233             var lc = this.doc.body.lastChild;
29234             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
29235                 // add an extra line at the end.
29236                 this.doc.body.appendChild(this.doc.createElement('br'));
29237             }
29238             
29239             
29240         }
29241     },
29242
29243     // private
29244     deferFocus : function(){
29245         this.focus.defer(10, this);
29246     },
29247
29248     // doc'ed in Field
29249     focus : function(){
29250         if(this.win && !this.sourceEditMode){
29251             this.win.focus();
29252         }else{
29253             this.el.focus();
29254         }
29255     },
29256     
29257     assignDocWin: function()
29258     {
29259         var iframe = this.iframe;
29260         
29261          if(Roo.isIE){
29262             this.doc = iframe.contentWindow.document;
29263             this.win = iframe.contentWindow;
29264         } else {
29265 //            if (!Roo.get(this.frameId)) {
29266 //                return;
29267 //            }
29268 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
29269 //            this.win = Roo.get(this.frameId).dom.contentWindow;
29270             
29271             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
29272                 return;
29273             }
29274             
29275             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
29276             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
29277         }
29278     },
29279     
29280     // private
29281     initEditor : function(){
29282         //console.log("INIT EDITOR");
29283         this.assignDocWin();
29284         
29285         
29286         
29287         this.doc.designMode="on";
29288         this.doc.open();
29289         this.doc.write(this.getDocMarkup());
29290         this.doc.close();
29291         
29292         var dbody = (this.doc.body || this.doc.documentElement);
29293         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
29294         // this copies styles from the containing element into thsi one..
29295         // not sure why we need all of this..
29296         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
29297         
29298         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
29299         //ss['background-attachment'] = 'fixed'; // w3c
29300         dbody.bgProperties = 'fixed'; // ie
29301         dbody.setAttribute("translate", "no");
29302         
29303         //Roo.DomHelper.applyStyles(dbody, ss);
29304         Roo.EventManager.on(this.doc, {
29305              
29306             'mouseup': this.onEditorEvent,
29307             'dblclick': this.onEditorEvent,
29308             'click': this.onEditorEvent,
29309             'keyup': this.onEditorEvent,
29310             
29311             buffer:100,
29312             scope: this
29313         });
29314         Roo.EventManager.on(this.doc, {
29315             'paste': this.onPasteEvent,
29316             scope : this
29317         });
29318         if(Roo.isGecko){
29319             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
29320         }
29321         //??? needed???
29322         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
29323             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
29324         }
29325         this.initialized = true;
29326
29327         
29328         // initialize special key events - enter
29329         new Roo.htmleditor.KeyEnter({core : this});
29330         
29331          
29332         
29333         this.owner.fireEvent('initialize', this);
29334         this.pushValue();
29335     },
29336     // this is to prevent a href clicks resulting in a redirect?
29337    
29338     onPasteEvent : function(e,v)
29339     {
29340         // I think we better assume paste is going to be a dirty load of rubish from word..
29341         
29342         // even pasting into a 'email version' of this widget will have to clean up that mess.
29343         var cd = (e.browserEvent.clipboardData || window.clipboardData);
29344         
29345         // check what type of paste - if it's an image, then handle it differently.
29346         if (cd.files && cd.files.length > 0) {
29347             // pasting images?
29348             var urlAPI = (window.createObjectURL && window) || 
29349                 (window.URL && URL.revokeObjectURL && URL) || 
29350                 (window.webkitURL && webkitURL);
29351     
29352             var url = urlAPI.createObjectURL( cd.files[0]);
29353             this.insertAtCursor('<img src=" + url + ">');
29354             return false;
29355         }
29356         if (cd.types.indexOf('text/html') < 0 ) {
29357             return false;
29358         }
29359         var images = [];
29360         var html = cd.getData('text/html'); // clipboard event
29361         if (cd.types.indexOf('text/rtf') > -1) {
29362             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
29363             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
29364         }
29365         //Roo.log(images);
29366         //Roo.log(imgs);
29367         // fixme..
29368         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
29369                        .map(function(g) { return g.toDataURL(); })
29370                        .filter(function(g) { return g != 'about:blank'; });
29371         
29372         
29373         html = this.cleanWordChars(html);
29374         
29375         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
29376         
29377         
29378         var sn = this.getParentElement();
29379         // check if d contains a table, and prevent nesting??
29380         //Roo.log(d.getElementsByTagName('table'));
29381         //Roo.log(sn);
29382         //Roo.log(sn.closest('table'));
29383         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
29384             e.preventDefault();
29385             this.insertAtCursor("You can not nest tables");
29386             //Roo.log("prevent?"); // fixme - 
29387             return false;
29388         }
29389         
29390         if (images.length > 0) {
29391             Roo.each(d.getElementsByTagName('img'), function(img, i) {
29392                 img.setAttribute('src', images[i]);
29393             });
29394         }
29395         if (this.autoClean) {
29396             new Roo.htmleditor.FilterWord({ node : d });
29397             
29398             new Roo.htmleditor.FilterStyleToTag({ node : d });
29399             new Roo.htmleditor.FilterAttributes({
29400                 node : d,
29401                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width'],
29402                 attrib_clean : ['href', 'src' ] 
29403             });
29404             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
29405             // should be fonts..
29406             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', 'O:P' ]} );
29407             new Roo.htmleditor.FilterParagraph({ node : d });
29408             new Roo.htmleditor.FilterSpan({ node : d });
29409             new Roo.htmleditor.FilterLongBr({ node : d });
29410             new Roo.htmleditor.FilterComment({ node : d });
29411             
29412             
29413         }
29414         if (this.enableBlocks) {
29415                 
29416             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
29417                 if (img.closest('figure')) { // assume!! that it's aready
29418                     return;
29419                 }
29420                 var fig  = new Roo.htmleditor.BlockFigure({
29421                     image_src  : img.src
29422                 });
29423                 fig.updateElement(img); // replace it..
29424                 
29425             });
29426         }
29427         
29428         
29429         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
29430         if (this.enableBlocks) {
29431             Roo.htmleditor.Block.initAll(this.doc.body);
29432         }
29433          
29434         
29435         e.preventDefault();
29436         return false;
29437         // default behaveiour should be our local cleanup paste? (optional?)
29438         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
29439         //this.owner.fireEvent('paste', e, v);
29440     },
29441     // private
29442     onDestroy : function(){
29443         
29444         
29445         
29446         if(this.rendered){
29447             
29448             //for (var i =0; i < this.toolbars.length;i++) {
29449             //    // fixme - ask toolbars for heights?
29450             //    this.toolbars[i].onDestroy();
29451            // }
29452             
29453             //this.wrap.dom.innerHTML = '';
29454             //this.wrap.remove();
29455         }
29456     },
29457
29458     // private
29459     onFirstFocus : function(){
29460         
29461         this.assignDocWin();
29462         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
29463         
29464         this.activated = true;
29465          
29466     
29467         if(Roo.isGecko){ // prevent silly gecko errors
29468             this.win.focus();
29469             var s = this.win.getSelection();
29470             if(!s.focusNode || s.focusNode.nodeType != 3){
29471                 var r = s.getRangeAt(0);
29472                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
29473                 r.collapse(true);
29474                 this.deferFocus();
29475             }
29476             try{
29477                 this.execCmd('useCSS', true);
29478                 this.execCmd('styleWithCSS', false);
29479             }catch(e){}
29480         }
29481         this.owner.fireEvent('activate', this);
29482     },
29483
29484     // private
29485     adjustFont: function(btn){
29486         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
29487         //if(Roo.isSafari){ // safari
29488         //    adjust *= 2;
29489        // }
29490         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
29491         if(Roo.isSafari){ // safari
29492             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
29493             v =  (v < 10) ? 10 : v;
29494             v =  (v > 48) ? 48 : v;
29495             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
29496             
29497         }
29498         
29499         
29500         v = Math.max(1, v+adjust);
29501         
29502         this.execCmd('FontSize', v  );
29503     },
29504
29505     onEditorEvent : function(e)
29506     {
29507          
29508         
29509         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
29510             return; // we do not handle this.. (undo manager does..)
29511         }
29512         // in theory this detects if the last element is not a br, then we try and do that.
29513         // its so clicking in space at bottom triggers adding a br and moving the cursor.
29514         if (e &&
29515             e.target.nodeName == 'BODY' &&
29516             e.type == "mouseup" &&
29517             this.doc.body.lastChild
29518            ) {
29519             var lc = this.doc.body.lastChild;
29520             // gtx-trans is google translate plugin adding crap.
29521             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
29522                 lc = lc.previousSibling;
29523             }
29524             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
29525             // if last element is <BR> - then dont do anything.
29526             
29527                 var ns = this.doc.createElement('br');
29528                 this.doc.body.appendChild(ns);
29529                 range = this.doc.createRange();
29530                 range.setStartAfter(ns);
29531                 range.collapse(true);
29532                 var sel = this.win.getSelection();
29533                 sel.removeAllRanges();
29534                 sel.addRange(range);
29535             }
29536         }
29537         
29538         
29539         
29540         this.fireEditorEvent(e);
29541       //  this.updateToolbar();
29542         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
29543     },
29544     
29545     fireEditorEvent: function(e)
29546     {
29547         this.owner.fireEvent('editorevent', this, e);
29548     },
29549
29550     insertTag : function(tg)
29551     {
29552         // could be a bit smarter... -> wrap the current selected tRoo..
29553         if (tg.toLowerCase() == 'span' ||
29554             tg.toLowerCase() == 'code' ||
29555             tg.toLowerCase() == 'sup' ||
29556             tg.toLowerCase() == 'sub' 
29557             ) {
29558             
29559             range = this.createRange(this.getSelection());
29560             var wrappingNode = this.doc.createElement(tg.toLowerCase());
29561             wrappingNode.appendChild(range.extractContents());
29562             range.insertNode(wrappingNode);
29563
29564             return;
29565             
29566             
29567             
29568         }
29569         this.execCmd("formatblock",   tg);
29570         this.undoManager.addEvent(); 
29571     },
29572     
29573     insertText : function(txt)
29574     {
29575         
29576         
29577         var range = this.createRange();
29578         range.deleteContents();
29579                //alert(Sender.getAttribute('label'));
29580                
29581         range.insertNode(this.doc.createTextNode(txt));
29582         this.undoManager.addEvent();
29583     } ,
29584     
29585      
29586
29587     /**
29588      * Executes a Midas editor command on the editor document and performs necessary focus and
29589      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
29590      * @param {String} cmd The Midas command
29591      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
29592      */
29593     relayCmd : function(cmd, value)
29594     {
29595         
29596         switch (cmd) {
29597             case 'justifyleft':
29598             case 'justifyright':
29599             case 'justifycenter':
29600                 // if we are in a cell, then we will adjust the
29601                 var n = this.getParentElement();
29602                 var td = n.closest('td');
29603                 if (td) {
29604                     var bl = Roo.htmleditor.Block.factory(td);
29605                     bl.textAlign = cmd.replace('justify','');
29606                     bl.updateElement();
29607                     this.owner.fireEvent('editorevent', this);
29608                     return;
29609                 }
29610                 this.execCmd('styleWithCSS', true); // 
29611                 break;
29612             case 'bold':
29613             case 'italic':
29614                 // if there is no selection, then we insert, and set the curson inside it..
29615                 this.execCmd('styleWithCSS', false); 
29616                 break;
29617                 
29618         
29619             default:
29620                 break;
29621         }
29622         
29623         
29624         this.win.focus();
29625         this.execCmd(cmd, value);
29626         this.owner.fireEvent('editorevent', this);
29627         //this.updateToolbar();
29628         this.owner.deferFocus();
29629     },
29630
29631     /**
29632      * Executes a Midas editor command directly on the editor document.
29633      * For visual commands, you should use {@link #relayCmd} instead.
29634      * <b>This should only be called after the editor is initialized.</b>
29635      * @param {String} cmd The Midas command
29636      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
29637      */
29638     execCmd : function(cmd, value){
29639         this.doc.execCommand(cmd, false, value === undefined ? null : value);
29640         this.syncValue();
29641     },
29642  
29643  
29644    
29645     /**
29646      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
29647      * to insert tRoo.
29648      * @param {String} text | dom node.. 
29649      */
29650     insertAtCursor : function(text)
29651     {
29652         
29653         if(!this.activated){
29654             return;
29655         }
29656          
29657         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
29658             this.win.focus();
29659             
29660             
29661             // from jquery ui (MIT licenced)
29662             var range, node;
29663             var win = this.win;
29664             
29665             if (win.getSelection && win.getSelection().getRangeAt) {
29666                 
29667                 // delete the existing?
29668                 
29669                 this.createRange(this.getSelection()).deleteContents();
29670                 range = win.getSelection().getRangeAt(0);
29671                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
29672                 range.insertNode(node);
29673                 range = range.cloneRange();
29674                 range.collapse(false);
29675                  
29676                 win.getSelection().removeAllRanges();
29677                 win.getSelection().addRange(range);
29678                 
29679                 
29680                 
29681             } else if (win.document.selection && win.document.selection.createRange) {
29682                 // no firefox support
29683                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
29684                 win.document.selection.createRange().pasteHTML(txt);
29685             
29686             } else {
29687                 // no firefox support
29688                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
29689                 this.execCmd('InsertHTML', txt);
29690             } 
29691             this.syncValue();
29692             
29693             this.deferFocus();
29694         }
29695     },
29696  // private
29697     mozKeyPress : function(e){
29698         if(e.ctrlKey){
29699             var c = e.getCharCode(), cmd;
29700           
29701             if(c > 0){
29702                 c = String.fromCharCode(c).toLowerCase();
29703                 switch(c){
29704                     case 'b':
29705                         cmd = 'bold';
29706                         break;
29707                     case 'i':
29708                         cmd = 'italic';
29709                         break;
29710                     
29711                     case 'u':
29712                         cmd = 'underline';
29713                         break;
29714                     
29715                     //case 'v':
29716                       //  this.cleanUpPaste.defer(100, this);
29717                       //  return;
29718                         
29719                 }
29720                 if(cmd){
29721                     
29722                     this.relayCmd(cmd);
29723                     //this.win.focus();
29724                     //this.execCmd(cmd);
29725                     //this.deferFocus();
29726                     e.preventDefault();
29727                 }
29728                 
29729             }
29730         }
29731     },
29732
29733     // private
29734     fixKeys : function(){ // load time branching for fastest keydown performance
29735         
29736         
29737         if(Roo.isIE){
29738             return function(e){
29739                 var k = e.getKey(), r;
29740                 if(k == e.TAB){
29741                     e.stopEvent();
29742                     r = this.doc.selection.createRange();
29743                     if(r){
29744                         r.collapse(true);
29745                         r.pasteHTML('&#160;&#160;&#160;&#160;');
29746                         this.deferFocus();
29747                     }
29748                     return;
29749                 }
29750                 /// this is handled by Roo.htmleditor.KeyEnter
29751                  /*
29752                 if(k == e.ENTER){
29753                     r = this.doc.selection.createRange();
29754                     if(r){
29755                         var target = r.parentElement();
29756                         if(!target || target.tagName.toLowerCase() != 'li'){
29757                             e.stopEvent();
29758                             r.pasteHTML('<br/>');
29759                             r.collapse(false);
29760                             r.select();
29761                         }
29762                     }
29763                 }
29764                 */
29765                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29766                 //    this.cleanUpPaste.defer(100, this);
29767                 //    return;
29768                 //}
29769                 
29770                 
29771             };
29772         }else if(Roo.isOpera){
29773             return function(e){
29774                 var k = e.getKey();
29775                 if(k == e.TAB){
29776                     e.stopEvent();
29777                     this.win.focus();
29778                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
29779                     this.deferFocus();
29780                 }
29781                
29782                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29783                 //    this.cleanUpPaste.defer(100, this);
29784                  //   return;
29785                 //}
29786                 
29787             };
29788         }else if(Roo.isSafari){
29789             return function(e){
29790                 var k = e.getKey();
29791                 
29792                 if(k == e.TAB){
29793                     e.stopEvent();
29794                     this.execCmd('InsertText','\t');
29795                     this.deferFocus();
29796                     return;
29797                 }
29798                  this.mozKeyPress(e);
29799                 
29800                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29801                  //   this.cleanUpPaste.defer(100, this);
29802                  //   return;
29803                // }
29804                 
29805              };
29806         }
29807     }(),
29808     
29809     getAllAncestors: function()
29810     {
29811         var p = this.getSelectedNode();
29812         var a = [];
29813         if (!p) {
29814             a.push(p); // push blank onto stack..
29815             p = this.getParentElement();
29816         }
29817         
29818         
29819         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
29820             a.push(p);
29821             p = p.parentNode;
29822         }
29823         a.push(this.doc.body);
29824         return a;
29825     },
29826     lastSel : false,
29827     lastSelNode : false,
29828     
29829     
29830     getSelection : function() 
29831     {
29832         this.assignDocWin();
29833         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
29834     },
29835     /**
29836      * Select a dom node
29837      * @param {DomElement} node the node to select
29838      */
29839     selectNode : function(node, collapse)
29840     {
29841         var nodeRange = node.ownerDocument.createRange();
29842         try {
29843             nodeRange.selectNode(node);
29844         } catch (e) {
29845             nodeRange.selectNodeContents(node);
29846         }
29847         if (collapse === true) {
29848             nodeRange.collapse(true);
29849         }
29850         //
29851         var s = this.win.getSelection();
29852         s.removeAllRanges();
29853         s.addRange(nodeRange);
29854     },
29855     
29856     getSelectedNode: function() 
29857     {
29858         // this may only work on Gecko!!!
29859         
29860         // should we cache this!!!!
29861         
29862          
29863          
29864         var range = this.createRange(this.getSelection()).cloneRange();
29865         
29866         if (Roo.isIE) {
29867             var parent = range.parentElement();
29868             while (true) {
29869                 var testRange = range.duplicate();
29870                 testRange.moveToElementText(parent);
29871                 if (testRange.inRange(range)) {
29872                     break;
29873                 }
29874                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
29875                     break;
29876                 }
29877                 parent = parent.parentElement;
29878             }
29879             return parent;
29880         }
29881         
29882         // is ancestor a text element.
29883         var ac =  range.commonAncestorContainer;
29884         if (ac.nodeType == 3) {
29885             ac = ac.parentNode;
29886         }
29887         
29888         var ar = ac.childNodes;
29889          
29890         var nodes = [];
29891         var other_nodes = [];
29892         var has_other_nodes = false;
29893         for (var i=0;i<ar.length;i++) {
29894             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
29895                 continue;
29896             }
29897             // fullly contained node.
29898             
29899             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
29900                 nodes.push(ar[i]);
29901                 continue;
29902             }
29903             
29904             // probably selected..
29905             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
29906                 other_nodes.push(ar[i]);
29907                 continue;
29908             }
29909             // outer..
29910             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
29911                 continue;
29912             }
29913             
29914             
29915             has_other_nodes = true;
29916         }
29917         if (!nodes.length && other_nodes.length) {
29918             nodes= other_nodes;
29919         }
29920         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
29921             return false;
29922         }
29923         
29924         return nodes[0];
29925     },
29926     
29927     
29928     createRange: function(sel)
29929     {
29930         // this has strange effects when using with 
29931         // top toolbar - not sure if it's a great idea.
29932         //this.editor.contentWindow.focus();
29933         if (typeof sel != "undefined") {
29934             try {
29935                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
29936             } catch(e) {
29937                 return this.doc.createRange();
29938             }
29939         } else {
29940             return this.doc.createRange();
29941         }
29942     },
29943     getParentElement: function()
29944     {
29945         
29946         this.assignDocWin();
29947         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
29948         
29949         var range = this.createRange(sel);
29950          
29951         try {
29952             var p = range.commonAncestorContainer;
29953             while (p.nodeType == 3) { // text node
29954                 p = p.parentNode;
29955             }
29956             return p;
29957         } catch (e) {
29958             return null;
29959         }
29960     
29961     },
29962     /***
29963      *
29964      * Range intersection.. the hard stuff...
29965      *  '-1' = before
29966      *  '0' = hits..
29967      *  '1' = after.
29968      *         [ -- selected range --- ]
29969      *   [fail]                        [fail]
29970      *
29971      *    basically..
29972      *      if end is before start or  hits it. fail.
29973      *      if start is after end or hits it fail.
29974      *
29975      *   if either hits (but other is outside. - then it's not 
29976      *   
29977      *    
29978      **/
29979     
29980     
29981     // @see http://www.thismuchiknow.co.uk/?p=64.
29982     rangeIntersectsNode : function(range, node)
29983     {
29984         var nodeRange = node.ownerDocument.createRange();
29985         try {
29986             nodeRange.selectNode(node);
29987         } catch (e) {
29988             nodeRange.selectNodeContents(node);
29989         }
29990     
29991         var rangeStartRange = range.cloneRange();
29992         rangeStartRange.collapse(true);
29993     
29994         var rangeEndRange = range.cloneRange();
29995         rangeEndRange.collapse(false);
29996     
29997         var nodeStartRange = nodeRange.cloneRange();
29998         nodeStartRange.collapse(true);
29999     
30000         var nodeEndRange = nodeRange.cloneRange();
30001         nodeEndRange.collapse(false);
30002     
30003         return rangeStartRange.compareBoundaryPoints(
30004                  Range.START_TO_START, nodeEndRange) == -1 &&
30005                rangeEndRange.compareBoundaryPoints(
30006                  Range.START_TO_START, nodeStartRange) == 1;
30007         
30008          
30009     },
30010     rangeCompareNode : function(range, node)
30011     {
30012         var nodeRange = node.ownerDocument.createRange();
30013         try {
30014             nodeRange.selectNode(node);
30015         } catch (e) {
30016             nodeRange.selectNodeContents(node);
30017         }
30018         
30019         
30020         range.collapse(true);
30021     
30022         nodeRange.collapse(true);
30023      
30024         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
30025         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
30026          
30027         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
30028         
30029         var nodeIsBefore   =  ss == 1;
30030         var nodeIsAfter    = ee == -1;
30031         
30032         if (nodeIsBefore && nodeIsAfter) {
30033             return 0; // outer
30034         }
30035         if (!nodeIsBefore && nodeIsAfter) {
30036             return 1; //right trailed.
30037         }
30038         
30039         if (nodeIsBefore && !nodeIsAfter) {
30040             return 2;  // left trailed.
30041         }
30042         // fully contined.
30043         return 3;
30044     },
30045  
30046     cleanWordChars : function(input) {// change the chars to hex code
30047         
30048        var swapCodes  = [ 
30049             [    8211, "&#8211;" ], 
30050             [    8212, "&#8212;" ], 
30051             [    8216,  "'" ],  
30052             [    8217, "'" ],  
30053             [    8220, '"' ],  
30054             [    8221, '"' ],  
30055             [    8226, "*" ],  
30056             [    8230, "..." ]
30057         ]; 
30058         var output = input;
30059         Roo.each(swapCodes, function(sw) { 
30060             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
30061             
30062             output = output.replace(swapper, sw[1]);
30063         });
30064         
30065         return output;
30066     },
30067     
30068      
30069     
30070         
30071     
30072     cleanUpChild : function (node)
30073     {
30074         
30075         new Roo.htmleditor.FilterComment({node : node});
30076         new Roo.htmleditor.FilterAttributes({
30077                 node : node,
30078                 attrib_black : this.ablack,
30079                 attrib_clean : this.aclean,
30080                 style_white : this.cwhite,
30081                 style_black : this.cblack
30082         });
30083         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
30084         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
30085          
30086         
30087     },
30088     
30089     /**
30090      * Clean up MS wordisms...
30091      * @deprecated - use filter directly
30092      */
30093     cleanWord : function(node)
30094     {
30095         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
30096         
30097     },
30098    
30099     
30100     /**
30101
30102      * @deprecated - use filters
30103      */
30104     cleanTableWidths : function(node)
30105     {
30106         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
30107         
30108  
30109     },
30110     
30111      
30112         
30113     applyBlacklists : function()
30114     {
30115         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
30116         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
30117         
30118         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
30119         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
30120         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
30121         
30122         this.white = [];
30123         this.black = [];
30124         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
30125             if (b.indexOf(tag) > -1) {
30126                 return;
30127             }
30128             this.white.push(tag);
30129             
30130         }, this);
30131         
30132         Roo.each(w, function(tag) {
30133             if (b.indexOf(tag) > -1) {
30134                 return;
30135             }
30136             if (this.white.indexOf(tag) > -1) {
30137                 return;
30138             }
30139             this.white.push(tag);
30140             
30141         }, this);
30142         
30143         
30144         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
30145             if (w.indexOf(tag) > -1) {
30146                 return;
30147             }
30148             this.black.push(tag);
30149             
30150         }, this);
30151         
30152         Roo.each(b, function(tag) {
30153             if (w.indexOf(tag) > -1) {
30154                 return;
30155             }
30156             if (this.black.indexOf(tag) > -1) {
30157                 return;
30158             }
30159             this.black.push(tag);
30160             
30161         }, this);
30162         
30163         
30164         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
30165         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
30166         
30167         this.cwhite = [];
30168         this.cblack = [];
30169         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
30170             if (b.indexOf(tag) > -1) {
30171                 return;
30172             }
30173             this.cwhite.push(tag);
30174             
30175         }, this);
30176         
30177         Roo.each(w, function(tag) {
30178             if (b.indexOf(tag) > -1) {
30179                 return;
30180             }
30181             if (this.cwhite.indexOf(tag) > -1) {
30182                 return;
30183             }
30184             this.cwhite.push(tag);
30185             
30186         }, this);
30187         
30188         
30189         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
30190             if (w.indexOf(tag) > -1) {
30191                 return;
30192             }
30193             this.cblack.push(tag);
30194             
30195         }, this);
30196         
30197         Roo.each(b, function(tag) {
30198             if (w.indexOf(tag) > -1) {
30199                 return;
30200             }
30201             if (this.cblack.indexOf(tag) > -1) {
30202                 return;
30203             }
30204             this.cblack.push(tag);
30205             
30206         }, this);
30207     },
30208     
30209     setStylesheets : function(stylesheets)
30210     {
30211         if(typeof(stylesheets) == 'string'){
30212             Roo.get(this.iframe.contentDocument.head).createChild({
30213                 tag : 'link',
30214                 rel : 'stylesheet',
30215                 type : 'text/css',
30216                 href : stylesheets
30217             });
30218             
30219             return;
30220         }
30221         var _this = this;
30222      
30223         Roo.each(stylesheets, function(s) {
30224             if(!s.length){
30225                 return;
30226             }
30227             
30228             Roo.get(_this.iframe.contentDocument.head).createChild({
30229                 tag : 'link',
30230                 rel : 'stylesheet',
30231                 type : 'text/css',
30232                 href : s
30233             });
30234         });
30235
30236         
30237     },
30238     
30239     
30240     updateLanguage : function()
30241     {
30242         if (!this.iframe || !this.iframe.contentDocument) {
30243             return;
30244         }
30245         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
30246     },
30247     
30248     
30249     removeStylesheets : function()
30250     {
30251         var _this = this;
30252         
30253         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
30254             s.remove();
30255         });
30256     },
30257     
30258     setStyle : function(style)
30259     {
30260         Roo.get(this.iframe.contentDocument.head).createChild({
30261             tag : 'style',
30262             type : 'text/css',
30263             html : style
30264         });
30265
30266         return;
30267     }
30268     
30269     // hide stuff that is not compatible
30270     /**
30271      * @event blur
30272      * @hide
30273      */
30274     /**
30275      * @event change
30276      * @hide
30277      */
30278     /**
30279      * @event focus
30280      * @hide
30281      */
30282     /**
30283      * @event specialkey
30284      * @hide
30285      */
30286     /**
30287      * @cfg {String} fieldClass @hide
30288      */
30289     /**
30290      * @cfg {String} focusClass @hide
30291      */
30292     /**
30293      * @cfg {String} autoCreate @hide
30294      */
30295     /**
30296      * @cfg {String} inputType @hide
30297      */
30298     /**
30299      * @cfg {String} invalidClass @hide
30300      */
30301     /**
30302      * @cfg {String} invalidText @hide
30303      */
30304     /**
30305      * @cfg {String} msgFx @hide
30306      */
30307     /**
30308      * @cfg {String} validateOnBlur @hide
30309      */
30310 });
30311
30312 Roo.HtmlEditorCore.white = [
30313         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
30314         
30315        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
30316        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
30317        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
30318        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
30319        'TABLE',   'UL',         'XMP', 
30320        
30321        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
30322       'THEAD',   'TR', 
30323      
30324       'DIR', 'MENU', 'OL', 'UL', 'DL',
30325        
30326       'EMBED',  'OBJECT'
30327 ];
30328
30329
30330 Roo.HtmlEditorCore.black = [
30331     //    'embed',  'object', // enable - backend responsiblity to clean thiese
30332         'APPLET', // 
30333         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
30334         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
30335         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
30336         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
30337         //'FONT' // CLEAN LATER..
30338         'COLGROUP', 'COL'   // messy tables.
30339         
30340         
30341 ];
30342 Roo.HtmlEditorCore.clean = [ // ?? needed???
30343      'SCRIPT', 'STYLE', 'TITLE', 'XML'
30344 ];
30345 Roo.HtmlEditorCore.tag_remove = [
30346     'FONT', 'TBODY'  
30347 ];
30348 // attributes..
30349
30350 Roo.HtmlEditorCore.ablack = [
30351     'on'
30352 ];
30353     
30354 Roo.HtmlEditorCore.aclean = [ 
30355     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
30356 ];
30357
30358 // protocols..
30359 Roo.HtmlEditorCore.pwhite= [
30360         'http',  'https',  'mailto'
30361 ];
30362
30363 // white listed style attributes.
30364 Roo.HtmlEditorCore.cwhite= [
30365       //  'text-align', /// default is to allow most things..
30366       
30367          
30368 //        'font-size'//??
30369 ];
30370
30371 // black listed style attributes.
30372 Roo.HtmlEditorCore.cblack= [
30373       //  'font-size' -- this can be set by the project 
30374 ];
30375
30376
30377
30378
30379     /*
30380  * - LGPL
30381  *
30382  * HtmlEditor
30383  * 
30384  */
30385
30386 /**
30387  * @class Roo.bootstrap.form.HtmlEditor
30388  * @extends Roo.bootstrap.form.TextArea
30389  * Bootstrap HtmlEditor class
30390
30391  * @constructor
30392  * Create a new HtmlEditor
30393  * @param {Object} config The config object
30394  */
30395
30396 Roo.bootstrap.form.HtmlEditor = function(config){
30397     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
30398     if (!this.toolbars) {
30399         this.toolbars = [];
30400     }
30401     
30402     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
30403     this.addEvents({
30404             /**
30405              * @event initialize
30406              * Fires when the editor is fully initialized (including the iframe)
30407              * @param {HtmlEditor} this
30408              */
30409             initialize: true,
30410             /**
30411              * @event activate
30412              * Fires when the editor is first receives the focus. Any insertion must wait
30413              * until after this event.
30414              * @param {HtmlEditor} this
30415              */
30416             activate: true,
30417              /**
30418              * @event beforesync
30419              * Fires before the textarea is updated with content from the editor iframe. Return false
30420              * to cancel the sync.
30421              * @param {HtmlEditor} this
30422              * @param {String} html
30423              */
30424             beforesync: true,
30425              /**
30426              * @event beforepush
30427              * Fires before the iframe editor is updated with content from the textarea. Return false
30428              * to cancel the push.
30429              * @param {HtmlEditor} this
30430              * @param {String} html
30431              */
30432             beforepush: true,
30433              /**
30434              * @event sync
30435              * Fires when the textarea is updated with content from the editor iframe.
30436              * @param {HtmlEditor} this
30437              * @param {String} html
30438              */
30439             sync: true,
30440              /**
30441              * @event push
30442              * Fires when the iframe editor is updated with content from the textarea.
30443              * @param {HtmlEditor} this
30444              * @param {String} html
30445              */
30446             push: true,
30447              /**
30448              * @event editmodechange
30449              * Fires when the editor switches edit modes
30450              * @param {HtmlEditor} this
30451              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
30452              */
30453             editmodechange: true,
30454             /**
30455              * @event editorevent
30456              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30457              * @param {HtmlEditor} this
30458              */
30459             editorevent: true,
30460             /**
30461              * @event firstfocus
30462              * Fires when on first focus - needed by toolbars..
30463              * @param {HtmlEditor} this
30464              */
30465             firstfocus: true,
30466             /**
30467              * @event autosave
30468              * Auto save the htmlEditor value as a file into Events
30469              * @param {HtmlEditor} this
30470              */
30471             autosave: true,
30472             /**
30473              * @event savedpreview
30474              * preview the saved version of htmlEditor
30475              * @param {HtmlEditor} this
30476              */
30477             savedpreview: true
30478         });
30479 };
30480
30481
30482 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
30483     
30484     
30485       /**
30486      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
30487      */
30488     toolbars : false,
30489     
30490      /**
30491     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
30492     */
30493     btns : [],
30494    
30495      /**
30496      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
30497      *                        Roo.resizable.
30498      */
30499     resizable : false,
30500      /**
30501      * @cfg {Number} height (in pixels)
30502      */   
30503     height: 300,
30504    /**
30505      * @cfg {Number} width (in pixels)
30506      */   
30507     width: false,
30508     
30509     /**
30510      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30511      * 
30512      */
30513     stylesheets: false,
30514     
30515     // id of frame..
30516     frameId: false,
30517     
30518     // private properties
30519     validationEvent : false,
30520     deferHeight: true,
30521     initialized : false,
30522     activated : false,
30523     
30524     onFocus : Roo.emptyFn,
30525     iframePad:3,
30526     hideMode:'offsets',
30527     
30528     tbContainer : false,
30529     
30530     bodyCls : '',
30531     
30532     toolbarContainer :function() {
30533         return this.wrap.select('.x-html-editor-tb',true).first();
30534     },
30535
30536     /**
30537      * Protected method that will not generally be called directly. It
30538      * is called when the editor creates its toolbar. Override this method if you need to
30539      * add custom toolbar buttons.
30540      * @param {HtmlEditor} editor
30541      */
30542     createToolbar : function(){
30543         Roo.log('renewing');
30544         Roo.log("create toolbars");
30545         
30546         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
30547         this.toolbars[0].render(this.toolbarContainer());
30548         
30549         return;
30550         
30551 //        if (!editor.toolbars || !editor.toolbars.length) {
30552 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
30553 //        }
30554 //        
30555 //        for (var i =0 ; i < editor.toolbars.length;i++) {
30556 //            editor.toolbars[i] = Roo.factory(
30557 //                    typeof(editor.toolbars[i]) == 'string' ?
30558 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
30559 //                Roo.bootstrap.form.HtmlEditor);
30560 //            editor.toolbars[i].init(editor);
30561 //        }
30562     },
30563
30564      
30565     // private
30566     onRender : function(ct, position)
30567     {
30568        // Roo.log("Call onRender: " + this.xtype);
30569         var _t = this;
30570         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
30571       
30572         this.wrap = this.inputEl().wrap({
30573             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
30574         });
30575         
30576         this.editorcore.onRender(ct, position);
30577          
30578         if (this.resizable) {
30579             this.resizeEl = new Roo.Resizable(this.wrap, {
30580                 pinned : true,
30581                 wrap: true,
30582                 dynamic : true,
30583                 minHeight : this.height,
30584                 height: this.height,
30585                 handles : this.resizable,
30586                 width: this.width,
30587                 listeners : {
30588                     resize : function(r, w, h) {
30589                         _t.onResize(w,h); // -something
30590                     }
30591                 }
30592             });
30593             
30594         }
30595         this.createToolbar(this);
30596        
30597         
30598         if(!this.width && this.resizable){
30599             this.setSize(this.wrap.getSize());
30600         }
30601         if (this.resizeEl) {
30602             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
30603             // should trigger onReize..
30604         }
30605         
30606     },
30607
30608     // private
30609     onResize : function(w, h)
30610     {
30611         Roo.log('resize: ' +w + ',' + h );
30612         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
30613         var ew = false;
30614         var eh = false;
30615         
30616         if(this.inputEl() ){
30617             if(typeof w == 'number'){
30618                 var aw = w - this.wrap.getFrameWidth('lr');
30619                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
30620                 ew = aw;
30621             }
30622             if(typeof h == 'number'){
30623                  var tbh = -11;  // fixme it needs to tool bar size!
30624                 for (var i =0; i < this.toolbars.length;i++) {
30625                     // fixme - ask toolbars for heights?
30626                     tbh += this.toolbars[i].el.getHeight();
30627                     //if (this.toolbars[i].footer) {
30628                     //    tbh += this.toolbars[i].footer.el.getHeight();
30629                     //}
30630                 }
30631               
30632                 
30633                 
30634                 
30635                 
30636                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
30637                 ah -= 5; // knock a few pixes off for look..
30638                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
30639                 var eh = ah;
30640             }
30641         }
30642         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
30643         this.editorcore.onResize(ew,eh);
30644         
30645     },
30646
30647     /**
30648      * Toggles the editor between standard and source edit mode.
30649      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
30650      */
30651     toggleSourceEdit : function(sourceEditMode)
30652     {
30653         this.editorcore.toggleSourceEdit(sourceEditMode);
30654         
30655         if(this.editorcore.sourceEditMode){
30656             Roo.log('editor - showing textarea');
30657             
30658 //            Roo.log('in');
30659 //            Roo.log(this.syncValue());
30660             this.syncValue();
30661             this.inputEl().removeClass(['hide', 'x-hidden']);
30662             this.inputEl().dom.removeAttribute('tabIndex');
30663             this.inputEl().focus();
30664         }else{
30665             Roo.log('editor - hiding textarea');
30666 //            Roo.log('out')
30667 //            Roo.log(this.pushValue()); 
30668             this.pushValue();
30669             
30670             this.inputEl().addClass(['hide', 'x-hidden']);
30671             this.inputEl().dom.setAttribute('tabIndex', -1);
30672             //this.deferFocus();
30673         }
30674          
30675         if(this.resizable){
30676             this.setSize(this.wrap.getSize());
30677         }
30678         
30679         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
30680     },
30681  
30682     // private (for BoxComponent)
30683     adjustSize : Roo.BoxComponent.prototype.adjustSize,
30684
30685     // private (for BoxComponent)
30686     getResizeEl : function(){
30687         return this.wrap;
30688     },
30689
30690     // private (for BoxComponent)
30691     getPositionEl : function(){
30692         return this.wrap;
30693     },
30694
30695     // private
30696     initEvents : function(){
30697         this.originalValue = this.getValue();
30698     },
30699
30700 //    /**
30701 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
30702 //     * @method
30703 //     */
30704 //    markInvalid : Roo.emptyFn,
30705 //    /**
30706 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
30707 //     * @method
30708 //     */
30709 //    clearInvalid : Roo.emptyFn,
30710
30711     setValue : function(v){
30712         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
30713         this.editorcore.pushValue();
30714     },
30715
30716      
30717     // private
30718     deferFocus : function(){
30719         this.focus.defer(10, this);
30720     },
30721
30722     // doc'ed in Field
30723     focus : function(){
30724         this.editorcore.focus();
30725         
30726     },
30727       
30728
30729     // private
30730     onDestroy : function(){
30731         
30732         
30733         
30734         if(this.rendered){
30735             
30736             for (var i =0; i < this.toolbars.length;i++) {
30737                 // fixme - ask toolbars for heights?
30738                 this.toolbars[i].onDestroy();
30739             }
30740             
30741             this.wrap.dom.innerHTML = '';
30742             this.wrap.remove();
30743         }
30744     },
30745
30746     // private
30747     onFirstFocus : function(){
30748         //Roo.log("onFirstFocus");
30749         this.editorcore.onFirstFocus();
30750          for (var i =0; i < this.toolbars.length;i++) {
30751             this.toolbars[i].onFirstFocus();
30752         }
30753         
30754     },
30755     
30756     // private
30757     syncValue : function()
30758     {   
30759         this.editorcore.syncValue();
30760     },
30761     
30762     pushValue : function()
30763     {   
30764         this.editorcore.pushValue();
30765     }
30766      
30767     
30768     // hide stuff that is not compatible
30769     /**
30770      * @event blur
30771      * @hide
30772      */
30773     /**
30774      * @event change
30775      * @hide
30776      */
30777     /**
30778      * @event focus
30779      * @hide
30780      */
30781     /**
30782      * @event specialkey
30783      * @hide
30784      */
30785     /**
30786      * @cfg {String} fieldClass @hide
30787      */
30788     /**
30789      * @cfg {String} focusClass @hide
30790      */
30791     /**
30792      * @cfg {String} autoCreate @hide
30793      */
30794     /**
30795      * @cfg {String} inputType @hide
30796      */
30797      
30798     /**
30799      * @cfg {String} invalidText @hide
30800      */
30801     /**
30802      * @cfg {String} msgFx @hide
30803      */
30804     /**
30805      * @cfg {String} validateOnBlur @hide
30806      */
30807 });
30808  
30809     
30810    
30811    
30812    
30813       
30814 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
30815 /**
30816  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
30817  * @parent Roo.bootstrap.form.HtmlEditor
30818  * @extends Roo.bootstrap.nav.Simplebar
30819  * Basic Toolbar
30820  * 
30821  * @example
30822  * Usage:
30823  *
30824  new Roo.bootstrap.form.HtmlEditor({
30825     ....
30826     toolbars : [
30827         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
30828             disable : { fonts: 1 , format: 1, ..., ... , ...],
30829             btns : [ .... ]
30830         })
30831     }
30832      
30833  * 
30834  * @cfg {Object} disable List of elements to disable..
30835  * @cfg {Array} btns List of additional buttons.
30836  * 
30837  * 
30838  * NEEDS Extra CSS? 
30839  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
30840  */
30841  
30842 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
30843 {
30844     
30845     Roo.apply(this, config);
30846     
30847     // default disabled, based on 'good practice'..
30848     this.disable = this.disable || {};
30849     Roo.applyIf(this.disable, {
30850         fontSize : true,
30851         colors : true,
30852         specialElements : true
30853     });
30854     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
30855     
30856     this.editor = config.editor;
30857     this.editorcore = config.editor.editorcore;
30858     
30859     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
30860     
30861     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
30862     // dont call parent... till later.
30863 }
30864 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
30865      
30866     bar : true,
30867     
30868     editor : false,
30869     editorcore : false,
30870     
30871     
30872     formats : [
30873         "p" ,  
30874         "h1","h2","h3","h4","h5","h6", 
30875         "pre", "code", 
30876         "abbr", "acronym", "address", "cite", "samp", "var",
30877         'div','span'
30878     ],
30879     
30880     onRender : function(ct, position)
30881     {
30882        // Roo.log("Call onRender: " + this.xtype);
30883         
30884        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
30885        Roo.log(this.el);
30886        this.el.dom.style.marginBottom = '0';
30887        var _this = this;
30888        var editorcore = this.editorcore;
30889        var editor= this.editor;
30890        
30891        var children = [];
30892        var btn = function(id,cmd , toggle, handler, html){
30893        
30894             var  event = toggle ? 'toggle' : 'click';
30895        
30896             var a = {
30897                 size : 'sm',
30898                 xtype: 'Button',
30899                 xns: Roo.bootstrap,
30900                 //glyphicon : id,
30901                 fa: id,
30902                 cmd : id || cmd,
30903                 enableToggle:toggle !== false,
30904                 html : html || '',
30905                 pressed : toggle ? false : null,
30906                 listeners : {}
30907             };
30908             a.listeners[toggle ? 'toggle' : 'click'] = function() {
30909                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
30910             };
30911             children.push(a);
30912             return a;
30913        }
30914        
30915     //    var cb_box = function...
30916         
30917         var style = {
30918                 xtype: 'Button',
30919                 size : 'sm',
30920                 xns: Roo.bootstrap,
30921                 fa : 'font',
30922                 //html : 'submit'
30923                 menu : {
30924                     xtype: 'Menu',
30925                     xns: Roo.bootstrap,
30926                     items:  []
30927                 }
30928         };
30929         Roo.each(this.formats, function(f) {
30930             style.menu.items.push({
30931                 xtype :'MenuItem',
30932                 xns: Roo.bootstrap,
30933                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
30934                 tagname : f,
30935                 listeners : {
30936                     click : function()
30937                     {
30938                         editorcore.insertTag(this.tagname);
30939                         editor.focus();
30940                     }
30941                 }
30942                 
30943             });
30944         });
30945         children.push(style);   
30946         
30947         btn('bold',false,true);
30948         btn('italic',false,true);
30949         btn('align-left', 'justifyleft',true);
30950         btn('align-center', 'justifycenter',true);
30951         btn('align-right' , 'justifyright',true);
30952         btn('link', false, false, function(btn) {
30953             //Roo.log("create link?");
30954             var url = prompt(this.createLinkText, this.defaultLinkValue);
30955             if(url && url != 'http:/'+'/'){
30956                 this.editorcore.relayCmd('createlink', url);
30957             }
30958         }),
30959         btn('list','insertunorderedlist',true);
30960         btn('pencil', false,true, function(btn){
30961                 Roo.log(this);
30962                 this.toggleSourceEdit(btn.pressed);
30963         });
30964         
30965         if (this.editor.btns.length > 0) {
30966             for (var i = 0; i<this.editor.btns.length; i++) {
30967                 children.push(this.editor.btns[i]);
30968             }
30969         }
30970         
30971         /*
30972         var cog = {
30973                 xtype: 'Button',
30974                 size : 'sm',
30975                 xns: Roo.bootstrap,
30976                 glyphicon : 'cog',
30977                 //html : 'submit'
30978                 menu : {
30979                     xtype: 'Menu',
30980                     xns: Roo.bootstrap,
30981                     items:  []
30982                 }
30983         };
30984         
30985         cog.menu.items.push({
30986             xtype :'MenuItem',
30987             xns: Roo.bootstrap,
30988             html : Clean styles,
30989             tagname : f,
30990             listeners : {
30991                 click : function()
30992                 {
30993                     editorcore.insertTag(this.tagname);
30994                     editor.focus();
30995                 }
30996             }
30997             
30998         });
30999        */
31000         
31001          
31002        this.xtype = 'NavSimplebar';
31003         
31004         for(var i=0;i< children.length;i++) {
31005             
31006             this.buttons.add(this.addxtypeChild(children[i]));
31007             
31008         }
31009         
31010         editor.on('editorevent', this.updateToolbar, this);
31011     },
31012     onBtnClick : function(id)
31013     {
31014        this.editorcore.relayCmd(id);
31015        this.editorcore.focus();
31016     },
31017     
31018     /**
31019      * Protected method that will not generally be called directly. It triggers
31020      * a toolbar update by reading the markup state of the current selection in the editor.
31021      */
31022     updateToolbar: function(){
31023
31024         if(!this.editorcore.activated){
31025             this.editor.onFirstFocus(); // is this neeed?
31026             return;
31027         }
31028
31029         var btns = this.buttons; 
31030         var doc = this.editorcore.doc;
31031         btns.get('bold').setActive(doc.queryCommandState('bold'));
31032         btns.get('italic').setActive(doc.queryCommandState('italic'));
31033         //btns.get('underline').setActive(doc.queryCommandState('underline'));
31034         
31035         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
31036         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
31037         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
31038         
31039         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
31040         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
31041          /*
31042         
31043         var ans = this.editorcore.getAllAncestors();
31044         if (this.formatCombo) {
31045             
31046             
31047             var store = this.formatCombo.store;
31048             this.formatCombo.setValue("");
31049             for (var i =0; i < ans.length;i++) {
31050                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
31051                     // select it..
31052                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
31053                     break;
31054                 }
31055             }
31056         }
31057         
31058         
31059         
31060         // hides menus... - so this cant be on a menu...
31061         Roo.bootstrap.MenuMgr.hideAll();
31062         */
31063         Roo.bootstrap.menu.Manager.hideAll();
31064         //this.editorsyncValue();
31065     },
31066     onFirstFocus: function() {
31067         this.buttons.each(function(item){
31068            item.enable();
31069         });
31070     },
31071     toggleSourceEdit : function(sourceEditMode){
31072         
31073           
31074         if(sourceEditMode){
31075             Roo.log("disabling buttons");
31076            this.buttons.each( function(item){
31077                 if(item.cmd != 'pencil'){
31078                     item.disable();
31079                 }
31080             });
31081           
31082         }else{
31083             Roo.log("enabling buttons");
31084             if(this.editorcore.initialized){
31085                 this.buttons.each( function(item){
31086                     item.enable();
31087                 });
31088             }
31089             
31090         }
31091         Roo.log("calling toggole on editor");
31092         // tell the editor that it's been pressed..
31093         this.editor.toggleSourceEdit(sourceEditMode);
31094        
31095     }
31096 });
31097
31098
31099
31100
31101  
31102 /*
31103  * - LGPL
31104  */
31105
31106 /**
31107  * @class Roo.bootstrap.form.Markdown
31108  * @extends Roo.bootstrap.form.TextArea
31109  * Bootstrap Showdown editable area
31110  * @cfg {string} content
31111  * 
31112  * @constructor
31113  * Create a new Showdown
31114  */
31115
31116 Roo.bootstrap.form.Markdown = function(config){
31117     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
31118    
31119 };
31120
31121 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
31122     
31123     editing :false,
31124     
31125     initEvents : function()
31126     {
31127         
31128         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
31129         this.markdownEl = this.el.createChild({
31130             cls : 'roo-markdown-area'
31131         });
31132         this.inputEl().addClass('d-none');
31133         if (this.getValue() == '') {
31134             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
31135             
31136         } else {
31137             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
31138         }
31139         this.markdownEl.on('click', this.toggleTextEdit, this);
31140         this.on('blur', this.toggleTextEdit, this);
31141         this.on('specialkey', this.resizeTextArea, this);
31142     },
31143     
31144     toggleTextEdit : function()
31145     {
31146         var sh = this.markdownEl.getHeight();
31147         this.inputEl().addClass('d-none');
31148         this.markdownEl.addClass('d-none');
31149         if (!this.editing) {
31150             // show editor?
31151             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
31152             this.inputEl().removeClass('d-none');
31153             this.inputEl().focus();
31154             this.editing = true;
31155             return;
31156         }
31157         // show showdown...
31158         this.updateMarkdown();
31159         this.markdownEl.removeClass('d-none');
31160         this.editing = false;
31161         return;
31162     },
31163     updateMarkdown : function()
31164     {
31165         if (this.getValue() == '') {
31166             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
31167             return;
31168         }
31169  
31170         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
31171     },
31172     
31173     resizeTextArea: function () {
31174         
31175         var sh = 100;
31176         Roo.log([sh, this.getValue().split("\n").length * 30]);
31177         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
31178     },
31179     setValue : function(val)
31180     {
31181         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
31182         if (!this.editing) {
31183             this.updateMarkdown();
31184         }
31185         
31186     },
31187     focus : function()
31188     {
31189         if (!this.editing) {
31190             this.toggleTextEdit();
31191         }
31192         
31193     }
31194
31195
31196 });/*
31197  * Based on:
31198  * Ext JS Library 1.1.1
31199  * Copyright(c) 2006-2007, Ext JS, LLC.
31200  *
31201  * Originally Released Under LGPL - original licence link has changed is not relivant.
31202  *
31203  * Fork - LGPL
31204  * <script type="text/javascript">
31205  */
31206  
31207 /**
31208  * @class Roo.bootstrap.PagingToolbar
31209  * @extends Roo.bootstrap.nav.Simplebar
31210  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31211  * @constructor
31212  * Create a new PagingToolbar
31213  * @param {Object} config The config object
31214  * @param {Roo.data.Store} store
31215  */
31216 Roo.bootstrap.PagingToolbar = function(config)
31217 {
31218     // old args format still supported... - xtype is prefered..
31219         // created from xtype...
31220     
31221     this.ds = config.dataSource;
31222     
31223     if (config.store && !this.ds) {
31224         this.store= Roo.factory(config.store, Roo.data);
31225         this.ds = this.store;
31226         this.ds.xmodule = this.xmodule || false;
31227     }
31228     
31229     this.toolbarItems = [];
31230     if (config.items) {
31231         this.toolbarItems = config.items;
31232     }
31233     
31234     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
31235     
31236     this.cursor = 0;
31237     
31238     if (this.ds) { 
31239         this.bind(this.ds);
31240     }
31241     
31242     if (Roo.bootstrap.version == 4) {
31243         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
31244     } else {
31245         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
31246     }
31247     
31248 };
31249
31250 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
31251     /**
31252      * @cfg {Roo.bootstrap.Button} buttons[]
31253      * Buttons for the toolbar
31254      */
31255      /**
31256      * @cfg {Roo.data.Store} store
31257      * The underlying data store providing the paged data
31258      */
31259     /**
31260      * @cfg {String/HTMLElement/Element} container
31261      * container The id or element that will contain the toolbar
31262      */
31263     /**
31264      * @cfg {Boolean} displayInfo
31265      * True to display the displayMsg (defaults to false)
31266      */
31267     /**
31268      * @cfg {Number} pageSize
31269      * The number of records to display per page (defaults to 20)
31270      */
31271     pageSize: 20,
31272     /**
31273      * @cfg {String} displayMsg
31274      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
31275      */
31276     displayMsg : 'Displaying {0} - {1} of {2}',
31277     /**
31278      * @cfg {String} emptyMsg
31279      * The message to display when no records are found (defaults to "No data to display")
31280      */
31281     emptyMsg : 'No data to display',
31282     /**
31283      * Customizable piece of the default paging text (defaults to "Page")
31284      * @type String
31285      */
31286     beforePageText : "Page",
31287     /**
31288      * Customizable piece of the default paging text (defaults to "of %0")
31289      * @type String
31290      */
31291     afterPageText : "of {0}",
31292     /**
31293      * Customizable piece of the default paging text (defaults to "First Page")
31294      * @type String
31295      */
31296     firstText : "First Page",
31297     /**
31298      * Customizable piece of the default paging text (defaults to "Previous Page")
31299      * @type String
31300      */
31301     prevText : "Previous Page",
31302     /**
31303      * Customizable piece of the default paging text (defaults to "Next Page")
31304      * @type String
31305      */
31306     nextText : "Next Page",
31307     /**
31308      * Customizable piece of the default paging text (defaults to "Last Page")
31309      * @type String
31310      */
31311     lastText : "Last Page",
31312     /**
31313      * Customizable piece of the default paging text (defaults to "Refresh")
31314      * @type String
31315      */
31316     refreshText : "Refresh",
31317
31318     buttons : false,
31319     // private
31320     onRender : function(ct, position) 
31321     {
31322         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
31323         this.navgroup.parentId = this.id;
31324         this.navgroup.onRender(this.el, null);
31325         // add the buttons to the navgroup
31326         
31327         if(this.displayInfo){
31328             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
31329             this.displayEl = this.el.select('.x-paging-info', true).first();
31330 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
31331 //            this.displayEl = navel.el.select('span',true).first();
31332         }
31333         
31334         var _this = this;
31335         
31336         if(this.buttons){
31337             Roo.each(_this.buttons, function(e){ // this might need to use render????
31338                Roo.factory(e).render(_this.el);
31339             });
31340         }
31341             
31342         Roo.each(_this.toolbarItems, function(e) {
31343             _this.navgroup.addItem(e);
31344         });
31345         
31346         
31347         this.first = this.navgroup.addItem({
31348             tooltip: this.firstText,
31349             cls: "prev btn-outline-secondary",
31350             html : ' <i class="fa fa-step-backward"></i>',
31351             disabled: true,
31352             preventDefault: true,
31353             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
31354         });
31355         
31356         this.prev =  this.navgroup.addItem({
31357             tooltip: this.prevText,
31358             cls: "prev btn-outline-secondary",
31359             html : ' <i class="fa fa-backward"></i>',
31360             disabled: true,
31361             preventDefault: true,
31362             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
31363         });
31364     //this.addSeparator();
31365         
31366         
31367         var field = this.navgroup.addItem( {
31368             tagtype : 'span',
31369             cls : 'x-paging-position  btn-outline-secondary',
31370              disabled: true,
31371             html : this.beforePageText  +
31372                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
31373                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
31374          } ); //?? escaped?
31375         
31376         this.field = field.el.select('input', true).first();
31377         this.field.on("keydown", this.onPagingKeydown, this);
31378         this.field.on("focus", function(){this.dom.select();});
31379     
31380     
31381         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
31382         //this.field.setHeight(18);
31383         //this.addSeparator();
31384         this.next = this.navgroup.addItem({
31385             tooltip: this.nextText,
31386             cls: "next btn-outline-secondary",
31387             html : ' <i class="fa fa-forward"></i>',
31388             disabled: true,
31389             preventDefault: true,
31390             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
31391         });
31392         this.last = this.navgroup.addItem({
31393             tooltip: this.lastText,
31394             html : ' <i class="fa fa-step-forward"></i>',
31395             cls: "next btn-outline-secondary",
31396             disabled: true,
31397             preventDefault: true,
31398             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
31399         });
31400     //this.addSeparator();
31401         this.loading = this.navgroup.addItem({
31402             tooltip: this.refreshText,
31403             cls: "btn-outline-secondary",
31404             html : ' <i class="fa fa-refresh"></i>',
31405             preventDefault: true,
31406             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
31407         });
31408         
31409     },
31410
31411     // private
31412     updateInfo : function(){
31413         if(this.displayEl){
31414             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
31415             var msg = count == 0 ?
31416                 this.emptyMsg :
31417                 String.format(
31418                     this.displayMsg,
31419                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
31420                 );
31421             this.displayEl.update(msg);
31422         }
31423     },
31424
31425     // private
31426     onLoad : function(ds, r, o)
31427     {
31428         this.cursor = o.params && o.params.start ? o.params.start : 0;
31429         
31430         var d = this.getPageData(),
31431             ap = d.activePage,
31432             ps = d.pages;
31433         
31434         
31435         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
31436         this.field.dom.value = ap;
31437         this.first.setDisabled(ap == 1);
31438         this.prev.setDisabled(ap == 1);
31439         this.next.setDisabled(ap == ps);
31440         this.last.setDisabled(ap == ps);
31441         this.loading.enable();
31442         this.updateInfo();
31443     },
31444
31445     // private
31446     getPageData : function(){
31447         var total = this.ds.getTotalCount();
31448         return {
31449             total : total,
31450             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
31451             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
31452         };
31453     },
31454
31455     // private
31456     onLoadError : function(proxy, o){
31457         this.loading.enable();
31458         if (this.ds.events.loadexception.listeners.length  < 2) {
31459             // nothing has been assigned to loadexception except this...
31460             // so 
31461             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
31462
31463         }
31464     },
31465
31466     // private
31467     onPagingKeydown : function(e){
31468         var k = e.getKey();
31469         var d = this.getPageData();
31470         if(k == e.RETURN){
31471             var v = this.field.dom.value, pageNum;
31472             if(!v || isNaN(pageNum = parseInt(v, 10))){
31473                 this.field.dom.value = d.activePage;
31474                 return;
31475             }
31476             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31477             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31478             e.stopEvent();
31479         }
31480         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))
31481         {
31482           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31483           this.field.dom.value = pageNum;
31484           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31485           e.stopEvent();
31486         }
31487         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31488         {
31489           var v = this.field.dom.value, pageNum; 
31490           var increment = (e.shiftKey) ? 10 : 1;
31491           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31492                 increment *= -1;
31493           }
31494           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31495             this.field.dom.value = d.activePage;
31496             return;
31497           }
31498           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31499           {
31500             this.field.dom.value = parseInt(v, 10) + increment;
31501             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31502             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31503           }
31504           e.stopEvent();
31505         }
31506     },
31507
31508     // private
31509     beforeLoad : function(){
31510         if(this.loading){
31511             this.loading.disable();
31512         }
31513     },
31514
31515     // private
31516     onClick : function(which){
31517         
31518         var ds = this.ds;
31519         if (!ds) {
31520             return;
31521         }
31522         
31523         switch(which){
31524             case "first":
31525                 ds.load({params:{start: 0, limit: this.pageSize}});
31526             break;
31527             case "prev":
31528                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31529             break;
31530             case "next":
31531                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31532             break;
31533             case "last":
31534                 var total = ds.getTotalCount();
31535                 var extra = total % this.pageSize;
31536                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31537                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31538             break;
31539             case "refresh":
31540                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31541             break;
31542         }
31543     },
31544
31545     /**
31546      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31547      * @param {Roo.data.Store} store The data store to unbind
31548      */
31549     unbind : function(ds){
31550         ds.un("beforeload", this.beforeLoad, this);
31551         ds.un("load", this.onLoad, this);
31552         ds.un("loadexception", this.onLoadError, this);
31553         ds.un("remove", this.updateInfo, this);
31554         ds.un("add", this.updateInfo, this);
31555         this.ds = undefined;
31556     },
31557
31558     /**
31559      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31560      * @param {Roo.data.Store} store The data store to bind
31561      */
31562     bind : function(ds){
31563         ds.on("beforeload", this.beforeLoad, this);
31564         ds.on("load", this.onLoad, this);
31565         ds.on("loadexception", this.onLoadError, this);
31566         ds.on("remove", this.updateInfo, this);
31567         ds.on("add", this.updateInfo, this);
31568         this.ds = ds;
31569     }
31570 });/*
31571  * - LGPL
31572  *
31573  * element
31574  * 
31575  */
31576
31577 /**
31578  * @class Roo.bootstrap.MessageBar
31579  * @extends Roo.bootstrap.Component
31580  * Bootstrap MessageBar class
31581  * @cfg {String} html contents of the MessageBar
31582  * @cfg {String} weight (info | success | warning | danger) default info
31583  * @cfg {String} beforeClass insert the bar before the given class
31584  * @cfg {Boolean} closable (true | false) default false
31585  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
31586  * 
31587  * @constructor
31588  * Create a new Element
31589  * @param {Object} config The config object
31590  */
31591
31592 Roo.bootstrap.MessageBar = function(config){
31593     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
31594 };
31595
31596 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
31597     
31598     html: '',
31599     weight: 'info',
31600     closable: false,
31601     fixed: false,
31602     beforeClass: 'bootstrap-sticky-wrap',
31603     
31604     getAutoCreate : function(){
31605         
31606         var cfg = {
31607             tag: 'div',
31608             cls: 'alert alert-dismissable alert-' + this.weight,
31609             cn: [
31610                 {
31611                     tag: 'span',
31612                     cls: 'message',
31613                     html: this.html || ''
31614                 }
31615             ]
31616         };
31617         
31618         if(this.fixed){
31619             cfg.cls += ' alert-messages-fixed';
31620         }
31621         
31622         if(this.closable){
31623             cfg.cn.push({
31624                 tag: 'button',
31625                 cls: 'close',
31626                 html: 'x'
31627             });
31628         }
31629         
31630         return cfg;
31631     },
31632     
31633     onRender : function(ct, position)
31634     {
31635         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
31636         
31637         if(!this.el){
31638             var cfg = Roo.apply({},  this.getAutoCreate());
31639             cfg.id = Roo.id();
31640             
31641             if (this.cls) {
31642                 cfg.cls += ' ' + this.cls;
31643             }
31644             if (this.style) {
31645                 cfg.style = this.style;
31646             }
31647             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
31648             
31649             this.el.setVisibilityMode(Roo.Element.DISPLAY);
31650         }
31651         
31652         this.el.select('>button.close').on('click', this.hide, this);
31653         
31654     },
31655     
31656     show : function()
31657     {
31658         if (!this.rendered) {
31659             this.render();
31660         }
31661         
31662         this.el.show();
31663         
31664         this.fireEvent('show', this);
31665         
31666     },
31667     
31668     hide : function()
31669     {
31670         if (!this.rendered) {
31671             this.render();
31672         }
31673         
31674         this.el.hide();
31675         
31676         this.fireEvent('hide', this);
31677     },
31678     
31679     update : function()
31680     {
31681 //        var e = this.el.dom.firstChild;
31682 //        
31683 //        if(this.closable){
31684 //            e = e.nextSibling;
31685 //        }
31686 //        
31687 //        e.data = this.html || '';
31688
31689         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
31690     }
31691    
31692 });
31693
31694  
31695
31696      /*
31697  * - LGPL
31698  *
31699  * Graph
31700  * 
31701  */
31702
31703
31704 /**
31705  * @class Roo.bootstrap.Graph
31706  * @extends Roo.bootstrap.Component
31707  * Bootstrap Graph class
31708 > Prameters
31709  -sm {number} sm 4
31710  -md {number} md 5
31711  @cfg {String} graphtype  bar | vbar | pie
31712  @cfg {number} g_x coodinator | centre x (pie)
31713  @cfg {number} g_y coodinator | centre y (pie)
31714  @cfg {number} g_r radius (pie)
31715  @cfg {number} g_height height of the chart (respected by all elements in the set)
31716  @cfg {number} g_width width of the chart (respected by all elements in the set)
31717  @cfg {Object} title The title of the chart
31718     
31719  -{Array}  values
31720  -opts (object) options for the chart 
31721      o {
31722      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
31723      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
31724      o vgutter (number)
31725      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.
31726      o stacked (boolean) whether or not to tread values as in a stacked bar chart
31727      o to
31728      o stretch (boolean)
31729      o }
31730  -opts (object) options for the pie
31731      o{
31732      o cut
31733      o startAngle (number)
31734      o endAngle (number)
31735      } 
31736  *
31737  * @constructor
31738  * Create a new Input
31739  * @param {Object} config The config object
31740  */
31741
31742 Roo.bootstrap.Graph = function(config){
31743     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
31744     
31745     this.addEvents({
31746         // img events
31747         /**
31748          * @event click
31749          * The img click event for the img.
31750          * @param {Roo.EventObject} e
31751          */
31752         "click" : true
31753     });
31754 };
31755
31756 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
31757     
31758     sm: 4,
31759     md: 5,
31760     graphtype: 'bar',
31761     g_height: 250,
31762     g_width: 400,
31763     g_x: 50,
31764     g_y: 50,
31765     g_r: 30,
31766     opts:{
31767         //g_colors: this.colors,
31768         g_type: 'soft',
31769         g_gutter: '20%'
31770
31771     },
31772     title : false,
31773
31774     getAutoCreate : function(){
31775         
31776         var cfg = {
31777             tag: 'div',
31778             html : null
31779         };
31780         
31781         
31782         return  cfg;
31783     },
31784
31785     onRender : function(ct,position){
31786         
31787         
31788         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
31789         
31790         if (typeof(Raphael) == 'undefined') {
31791             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
31792             return;
31793         }
31794         
31795         this.raphael = Raphael(this.el.dom);
31796         
31797                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31798                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31799                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31800                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
31801                 /*
31802                 r.text(160, 10, "Single Series Chart").attr(txtattr);
31803                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
31804                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
31805                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
31806                 
31807                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
31808                 r.barchart(330, 10, 300, 220, data1);
31809                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
31810                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
31811                 */
31812                 
31813                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
31814                 // r.barchart(30, 30, 560, 250,  xdata, {
31815                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
31816                 //     axis : "0 0 1 1",
31817                 //     axisxlabels :  xdata
31818                 //     //yvalues : cols,
31819                    
31820                 // });
31821 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
31822 //        
31823 //        this.load(null,xdata,{
31824 //                axis : "0 0 1 1",
31825 //                axisxlabels :  xdata
31826 //                });
31827
31828     },
31829
31830     load : function(graphtype,xdata,opts)
31831     {
31832         this.raphael.clear();
31833         if(!graphtype) {
31834             graphtype = this.graphtype;
31835         }
31836         if(!opts){
31837             opts = this.opts;
31838         }
31839         var r = this.raphael,
31840             fin = function () {
31841                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
31842             },
31843             fout = function () {
31844                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
31845             },
31846             pfin = function() {
31847                 this.sector.stop();
31848                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
31849
31850                 if (this.label) {
31851                     this.label[0].stop();
31852                     this.label[0].attr({ r: 7.5 });
31853                     this.label[1].attr({ "font-weight": 800 });
31854                 }
31855             },
31856             pfout = function() {
31857                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
31858
31859                 if (this.label) {
31860                     this.label[0].animate({ r: 5 }, 500, "bounce");
31861                     this.label[1].attr({ "font-weight": 400 });
31862                 }
31863             };
31864
31865         switch(graphtype){
31866             case 'bar':
31867                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
31868                 break;
31869             case 'hbar':
31870                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
31871                 break;
31872             case 'pie':
31873 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
31874 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
31875 //            
31876                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
31877                 
31878                 break;
31879
31880         }
31881         
31882         if(this.title){
31883             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
31884         }
31885         
31886     },
31887     
31888     setTitle: function(o)
31889     {
31890         this.title = o;
31891     },
31892     
31893     initEvents: function() {
31894         
31895         if(!this.href){
31896             this.el.on('click', this.onClick, this);
31897         }
31898     },
31899     
31900     onClick : function(e)
31901     {
31902         Roo.log('img onclick');
31903         this.fireEvent('click', this, e);
31904     }
31905    
31906 });
31907
31908  
31909 Roo.bootstrap.dash = {};/*
31910  * - LGPL
31911  *
31912  * numberBox
31913  * 
31914  */
31915 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
31916
31917 /**
31918  * @class Roo.bootstrap.dash.NumberBox
31919  * @extends Roo.bootstrap.Component
31920  * Bootstrap NumberBox class
31921  * @cfg {String} headline Box headline
31922  * @cfg {String} content Box content
31923  * @cfg {String} icon Box icon
31924  * @cfg {String} footer Footer text
31925  * @cfg {String} fhref Footer href
31926  * 
31927  * @constructor
31928  * Create a new NumberBox
31929  * @param {Object} config The config object
31930  */
31931
31932
31933 Roo.bootstrap.dash.NumberBox = function(config){
31934     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
31935     
31936 };
31937
31938 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
31939     
31940     headline : '',
31941     content : '',
31942     icon : '',
31943     footer : '',
31944     fhref : '',
31945     ficon : '',
31946     
31947     getAutoCreate : function(){
31948         
31949         var cfg = {
31950             tag : 'div',
31951             cls : 'small-box ',
31952             cn : [
31953                 {
31954                     tag : 'div',
31955                     cls : 'inner',
31956                     cn :[
31957                         {
31958                             tag : 'h3',
31959                             cls : 'roo-headline',
31960                             html : this.headline
31961                         },
31962                         {
31963                             tag : 'p',
31964                             cls : 'roo-content',
31965                             html : this.content
31966                         }
31967                     ]
31968                 }
31969             ]
31970         };
31971         
31972         if(this.icon){
31973             cfg.cn.push({
31974                 tag : 'div',
31975                 cls : 'icon',
31976                 cn :[
31977                     {
31978                         tag : 'i',
31979                         cls : 'ion ' + this.icon
31980                     }
31981                 ]
31982             });
31983         }
31984         
31985         if(this.footer){
31986             var footer = {
31987                 tag : 'a',
31988                 cls : 'small-box-footer',
31989                 href : this.fhref || '#',
31990                 html : this.footer
31991             };
31992             
31993             cfg.cn.push(footer);
31994             
31995         }
31996         
31997         return  cfg;
31998     },
31999
32000     onRender : function(ct,position){
32001         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
32002
32003
32004        
32005                 
32006     },
32007
32008     setHeadline: function (value)
32009     {
32010         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
32011     },
32012     
32013     setFooter: function (value, href)
32014     {
32015         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
32016         
32017         if(href){
32018             this.el.select('a.small-box-footer',true).first().attr('href', href);
32019         }
32020         
32021     },
32022
32023     setContent: function (value)
32024     {
32025         this.el.select('.roo-content',true).first().dom.innerHTML = value;
32026     },
32027
32028     initEvents: function() 
32029     {   
32030         
32031     }
32032     
32033 });
32034
32035  
32036 /*
32037  * - LGPL
32038  *
32039  * TabBox
32040  * 
32041  */
32042 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
32043
32044 /**
32045  * @class Roo.bootstrap.dash.TabBox
32046  * @extends Roo.bootstrap.Component
32047  * @children Roo.bootstrap.dash.TabPane
32048  * Bootstrap TabBox class
32049  * @cfg {String} title Title of the TabBox
32050  * @cfg {String} icon Icon of the TabBox
32051  * @cfg {Boolean} showtabs (true|false) show the tabs default true
32052  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
32053  * 
32054  * @constructor
32055  * Create a new TabBox
32056  * @param {Object} config The config object
32057  */
32058
32059
32060 Roo.bootstrap.dash.TabBox = function(config){
32061     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
32062     this.addEvents({
32063         // raw events
32064         /**
32065          * @event addpane
32066          * When a pane is added
32067          * @param {Roo.bootstrap.dash.TabPane} pane
32068          */
32069         "addpane" : true,
32070         /**
32071          * @event activatepane
32072          * When a pane is activated
32073          * @param {Roo.bootstrap.dash.TabPane} pane
32074          */
32075         "activatepane" : true
32076         
32077          
32078     });
32079     
32080     this.panes = [];
32081 };
32082
32083 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
32084
32085     title : '',
32086     icon : false,
32087     showtabs : true,
32088     tabScrollable : false,
32089     
32090     getChildContainer : function()
32091     {
32092         return this.el.select('.tab-content', true).first();
32093     },
32094     
32095     getAutoCreate : function(){
32096         
32097         var header = {
32098             tag: 'li',
32099             cls: 'pull-left header',
32100             html: this.title,
32101             cn : []
32102         };
32103         
32104         if(this.icon){
32105             header.cn.push({
32106                 tag: 'i',
32107                 cls: 'fa ' + this.icon
32108             });
32109         }
32110         
32111         var h = {
32112             tag: 'ul',
32113             cls: 'nav nav-tabs pull-right',
32114             cn: [
32115                 header
32116             ]
32117         };
32118         
32119         if(this.tabScrollable){
32120             h = {
32121                 tag: 'div',
32122                 cls: 'tab-header',
32123                 cn: [
32124                     {
32125                         tag: 'ul',
32126                         cls: 'nav nav-tabs pull-right',
32127                         cn: [
32128                             header
32129                         ]
32130                     }
32131                 ]
32132             };
32133         }
32134         
32135         var cfg = {
32136             tag: 'div',
32137             cls: 'nav-tabs-custom',
32138             cn: [
32139                 h,
32140                 {
32141                     tag: 'div',
32142                     cls: 'tab-content no-padding',
32143                     cn: []
32144                 }
32145             ]
32146         };
32147
32148         return  cfg;
32149     },
32150     initEvents : function()
32151     {
32152         //Roo.log('add add pane handler');
32153         this.on('addpane', this.onAddPane, this);
32154     },
32155      /**
32156      * Updates the box title
32157      * @param {String} html to set the title to.
32158      */
32159     setTitle : function(value)
32160     {
32161         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
32162     },
32163     onAddPane : function(pane)
32164     {
32165         this.panes.push(pane);
32166         //Roo.log('addpane');
32167         //Roo.log(pane);
32168         // tabs are rendere left to right..
32169         if(!this.showtabs){
32170             return;
32171         }
32172         
32173         var ctr = this.el.select('.nav-tabs', true).first();
32174          
32175          
32176         var existing = ctr.select('.nav-tab',true);
32177         var qty = existing.getCount();;
32178         
32179         
32180         var tab = ctr.createChild({
32181             tag : 'li',
32182             cls : 'nav-tab' + (qty ? '' : ' active'),
32183             cn : [
32184                 {
32185                     tag : 'a',
32186                     href:'#',
32187                     html : pane.title
32188                 }
32189             ]
32190         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
32191         pane.tab = tab;
32192         
32193         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
32194         if (!qty) {
32195             pane.el.addClass('active');
32196         }
32197         
32198                 
32199     },
32200     onTabClick : function(ev,un,ob,pane)
32201     {
32202         //Roo.log('tab - prev default');
32203         ev.preventDefault();
32204         
32205         
32206         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
32207         pane.tab.addClass('active');
32208         //Roo.log(pane.title);
32209         this.getChildContainer().select('.tab-pane',true).removeClass('active');
32210         // technically we should have a deactivate event.. but maybe add later.
32211         // and it should not de-activate the selected tab...
32212         this.fireEvent('activatepane', pane);
32213         pane.el.addClass('active');
32214         pane.fireEvent('activate');
32215         
32216         
32217     },
32218     
32219     getActivePane : function()
32220     {
32221         var r = false;
32222         Roo.each(this.panes, function(p) {
32223             if(p.el.hasClass('active')){
32224                 r = p;
32225                 return false;
32226             }
32227             
32228             return;
32229         });
32230         
32231         return r;
32232     }
32233     
32234     
32235 });
32236
32237  
32238 /*
32239  * - LGPL
32240  *
32241  * Tab pane
32242  * 
32243  */
32244 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
32245 /**
32246  * @class Roo.bootstrap.TabPane
32247  * @extends Roo.bootstrap.Component
32248  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
32249  * Bootstrap TabPane class
32250  * @cfg {Boolean} active (false | true) Default false
32251  * @cfg {String} title title of panel
32252
32253  * 
32254  * @constructor
32255  * Create a new TabPane
32256  * @param {Object} config The config object
32257  */
32258
32259 Roo.bootstrap.dash.TabPane = function(config){
32260     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
32261     
32262     this.addEvents({
32263         // raw events
32264         /**
32265          * @event activate
32266          * When a pane is activated
32267          * @param {Roo.bootstrap.dash.TabPane} pane
32268          */
32269         "activate" : true
32270          
32271     });
32272 };
32273
32274 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
32275     
32276     active : false,
32277     title : '',
32278     
32279     // the tabBox that this is attached to.
32280     tab : false,
32281      
32282     getAutoCreate : function() 
32283     {
32284         var cfg = {
32285             tag: 'div',
32286             cls: 'tab-pane'
32287         };
32288         
32289         if(this.active){
32290             cfg.cls += ' active';
32291         }
32292         
32293         return cfg;
32294     },
32295     initEvents  : function()
32296     {
32297         //Roo.log('trigger add pane handler');
32298         this.parent().fireEvent('addpane', this)
32299     },
32300     
32301      /**
32302      * Updates the tab title 
32303      * @param {String} html to set the title to.
32304      */
32305     setTitle: function(str)
32306     {
32307         if (!this.tab) {
32308             return;
32309         }
32310         this.title = str;
32311         this.tab.select('a', true).first().dom.innerHTML = str;
32312         
32313     }
32314     
32315     
32316     
32317 });
32318
32319  
32320
32321
32322  /*
32323  * - LGPL
32324  *
32325  * Tooltip
32326  * 
32327  */
32328
32329 /**
32330  * @class Roo.bootstrap.Tooltip
32331  * Bootstrap Tooltip class
32332  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
32333  * to determine which dom element triggers the tooltip.
32334  * 
32335  * It needs to add support for additional attributes like tooltip-position
32336  * 
32337  * @constructor
32338  * Create a new Toolti
32339  * @param {Object} config The config object
32340  */
32341
32342 Roo.bootstrap.Tooltip = function(config){
32343     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
32344     
32345     this.alignment = Roo.bootstrap.Tooltip.alignment;
32346     
32347     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
32348         this.alignment = config.alignment;
32349     }
32350     
32351 };
32352
32353 Roo.apply(Roo.bootstrap.Tooltip, {
32354     /**
32355      * @function init initialize tooltip monitoring.
32356      * @static
32357      */
32358     currentEl : false,
32359     currentTip : false,
32360     currentRegion : false,
32361     
32362     //  init : delay?
32363     
32364     init : function()
32365     {
32366         Roo.get(document).on('mouseover', this.enter ,this);
32367         Roo.get(document).on('mouseout', this.leave, this);
32368          
32369         
32370         this.currentTip = new Roo.bootstrap.Tooltip();
32371     },
32372     
32373     enter : function(ev)
32374     {
32375         var dom = ev.getTarget();
32376         
32377         //Roo.log(['enter',dom]);
32378         var el = Roo.fly(dom);
32379         if (this.currentEl) {
32380             //Roo.log(dom);
32381             //Roo.log(this.currentEl);
32382             //Roo.log(this.currentEl.contains(dom));
32383             if (this.currentEl == el) {
32384                 return;
32385             }
32386             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
32387                 return;
32388             }
32389
32390         }
32391         
32392         if (this.currentTip.el) {
32393             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
32394         }    
32395         //Roo.log(ev);
32396         
32397         if(!el || el.dom == document){
32398             return;
32399         }
32400         
32401         var bindEl = el; 
32402         var pel = false;
32403         if (!el.attr('tooltip')) {
32404             pel = el.findParent("[tooltip]");
32405             if (pel) {
32406                 bindEl = Roo.get(pel);
32407             }
32408         }
32409         
32410        
32411         
32412         // you can not look for children, as if el is the body.. then everythign is the child..
32413         if (!pel && !el.attr('tooltip')) { //
32414             if (!el.select("[tooltip]").elements.length) {
32415                 return;
32416             }
32417             // is the mouse over this child...?
32418             bindEl = el.select("[tooltip]").first();
32419             var xy = ev.getXY();
32420             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
32421                 //Roo.log("not in region.");
32422                 return;
32423             }
32424             //Roo.log("child element over..");
32425             
32426         }
32427         this.currentEl = el;
32428         this.currentTip.bind(bindEl);
32429         this.currentRegion = Roo.lib.Region.getRegion(dom);
32430         this.currentTip.enter();
32431         
32432     },
32433     leave : function(ev)
32434     {
32435         var dom = ev.getTarget();
32436         //Roo.log(['leave',dom]);
32437         if (!this.currentEl) {
32438             return;
32439         }
32440         
32441         
32442         if (dom != this.currentEl.dom) {
32443             return;
32444         }
32445         var xy = ev.getXY();
32446         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
32447             return;
32448         }
32449         // only activate leave if mouse cursor is outside... bounding box..
32450         
32451         
32452         
32453         
32454         if (this.currentTip) {
32455             this.currentTip.leave();
32456         }
32457         //Roo.log('clear currentEl');
32458         this.currentEl = false;
32459         
32460         
32461     },
32462     alignment : {
32463         'left' : ['r-l', [-2,0], 'right'],
32464         'right' : ['l-r', [2,0], 'left'],
32465         'bottom' : ['t-b', [0,2], 'top'],
32466         'top' : [ 'b-t', [0,-2], 'bottom']
32467     }
32468     
32469 });
32470
32471
32472 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
32473     
32474     
32475     bindEl : false,
32476     
32477     delay : null, // can be { show : 300 , hide: 500}
32478     
32479     timeout : null,
32480     
32481     hoverState : null, //???
32482     
32483     placement : 'bottom', 
32484     
32485     alignment : false,
32486     
32487     getAutoCreate : function(){
32488     
32489         var cfg = {
32490            cls : 'tooltip',   
32491            role : 'tooltip',
32492            cn : [
32493                 {
32494                     cls : 'tooltip-arrow arrow'
32495                 },
32496                 {
32497                     cls : 'tooltip-inner'
32498                 }
32499            ]
32500         };
32501         
32502         return cfg;
32503     },
32504     bind : function(el)
32505     {
32506         this.bindEl = el;
32507     },
32508     
32509     initEvents : function()
32510     {
32511         this.arrowEl = this.el.select('.arrow', true).first();
32512         this.innerEl = this.el.select('.tooltip-inner', true).first();
32513     },
32514     
32515     enter : function () {
32516        
32517         if (this.timeout != null) {
32518             clearTimeout(this.timeout);
32519         }
32520         
32521         this.hoverState = 'in';
32522          //Roo.log("enter - show");
32523         if (!this.delay || !this.delay.show) {
32524             this.show();
32525             return;
32526         }
32527         var _t = this;
32528         this.timeout = setTimeout(function () {
32529             if (_t.hoverState == 'in') {
32530                 _t.show();
32531             }
32532         }, this.delay.show);
32533     },
32534     leave : function()
32535     {
32536         clearTimeout(this.timeout);
32537     
32538         this.hoverState = 'out';
32539          if (!this.delay || !this.delay.hide) {
32540             this.hide();
32541             return;
32542         }
32543        
32544         var _t = this;
32545         this.timeout = setTimeout(function () {
32546             //Roo.log("leave - timeout");
32547             
32548             if (_t.hoverState == 'out') {
32549                 _t.hide();
32550                 Roo.bootstrap.Tooltip.currentEl = false;
32551             }
32552         }, delay);
32553     },
32554     
32555     show : function (msg)
32556     {
32557         if (!this.el) {
32558             this.render(document.body);
32559         }
32560         // set content.
32561         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
32562         
32563         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
32564         
32565         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
32566         
32567         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
32568                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
32569         
32570         var placement = typeof this.placement == 'function' ?
32571             this.placement.call(this, this.el, on_el) :
32572             this.placement;
32573             
32574         var autoToken = /\s?auto?\s?/i;
32575         var autoPlace = autoToken.test(placement);
32576         if (autoPlace) {
32577             placement = placement.replace(autoToken, '') || 'top';
32578         }
32579         
32580         //this.el.detach()
32581         //this.el.setXY([0,0]);
32582         this.el.show();
32583         //this.el.dom.style.display='block';
32584         
32585         //this.el.appendTo(on_el);
32586         
32587         var p = this.getPosition();
32588         var box = this.el.getBox();
32589         
32590         if (autoPlace) {
32591             // fixme..
32592         }
32593         
32594         var align = this.alignment[placement];
32595         
32596         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
32597         
32598         if(placement == 'top' || placement == 'bottom'){
32599             if(xy[0] < 0){
32600                 placement = 'right';
32601             }
32602             
32603             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
32604                 placement = 'left';
32605             }
32606             
32607             var scroll = Roo.select('body', true).first().getScroll();
32608             
32609             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
32610                 placement = 'top';
32611             }
32612             
32613             align = this.alignment[placement];
32614             
32615             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
32616             
32617         }
32618         
32619         var elems = document.getElementsByTagName('div');
32620         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
32621         for (var i = 0; i < elems.length; i++) {
32622           var zindex = Number.parseInt(
32623                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
32624                 10
32625           );
32626           if (zindex > highest) {
32627             highest = zindex;
32628           }
32629         }
32630         
32631         
32632         
32633         this.el.dom.style.zIndex = highest;
32634         
32635         this.el.alignTo(this.bindEl, align[0],align[1]);
32636         //var arrow = this.el.select('.arrow',true).first();
32637         //arrow.set(align[2], 
32638         
32639         this.el.addClass(placement);
32640         this.el.addClass("bs-tooltip-"+ placement);
32641         
32642         this.el.addClass('in fade show');
32643         
32644         this.hoverState = null;
32645         
32646         if (this.el.hasClass('fade')) {
32647             // fade it?
32648         }
32649         
32650         
32651         
32652         
32653         
32654     },
32655     hide : function()
32656     {
32657          
32658         if (!this.el) {
32659             return;
32660         }
32661         //this.el.setXY([0,0]);
32662         this.el.removeClass(['show', 'in']);
32663         //this.el.hide();
32664         
32665     }
32666     
32667 });
32668  
32669
32670  /*
32671  * - LGPL
32672  *
32673  * Location Picker
32674  * 
32675  */
32676
32677 /**
32678  * @class Roo.bootstrap.LocationPicker
32679  * @extends Roo.bootstrap.Component
32680  * Bootstrap LocationPicker class
32681  * @cfg {Number} latitude Position when init default 0
32682  * @cfg {Number} longitude Position when init default 0
32683  * @cfg {Number} zoom default 15
32684  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
32685  * @cfg {Boolean} mapTypeControl default false
32686  * @cfg {Boolean} disableDoubleClickZoom default false
32687  * @cfg {Boolean} scrollwheel default true
32688  * @cfg {Boolean} streetViewControl default false
32689  * @cfg {Number} radius default 0
32690  * @cfg {String} locationName
32691  * @cfg {Boolean} draggable default true
32692  * @cfg {Boolean} enableAutocomplete default false
32693  * @cfg {Boolean} enableReverseGeocode default true
32694  * @cfg {String} markerTitle
32695  * 
32696  * @constructor
32697  * Create a new LocationPicker
32698  * @param {Object} config The config object
32699  */
32700
32701
32702 Roo.bootstrap.LocationPicker = function(config){
32703     
32704     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
32705     
32706     this.addEvents({
32707         /**
32708          * @event initial
32709          * Fires when the picker initialized.
32710          * @param {Roo.bootstrap.LocationPicker} this
32711          * @param {Google Location} location
32712          */
32713         initial : true,
32714         /**
32715          * @event positionchanged
32716          * Fires when the picker position changed.
32717          * @param {Roo.bootstrap.LocationPicker} this
32718          * @param {Google Location} location
32719          */
32720         positionchanged : true,
32721         /**
32722          * @event resize
32723          * Fires when the map resize.
32724          * @param {Roo.bootstrap.LocationPicker} this
32725          */
32726         resize : true,
32727         /**
32728          * @event show
32729          * Fires when the map show.
32730          * @param {Roo.bootstrap.LocationPicker} this
32731          */
32732         show : true,
32733         /**
32734          * @event hide
32735          * Fires when the map hide.
32736          * @param {Roo.bootstrap.LocationPicker} this
32737          */
32738         hide : true,
32739         /**
32740          * @event mapClick
32741          * Fires when click the map.
32742          * @param {Roo.bootstrap.LocationPicker} this
32743          * @param {Map event} e
32744          */
32745         mapClick : true,
32746         /**
32747          * @event mapRightClick
32748          * Fires when right click the map.
32749          * @param {Roo.bootstrap.LocationPicker} this
32750          * @param {Map event} e
32751          */
32752         mapRightClick : true,
32753         /**
32754          * @event markerClick
32755          * Fires when click the marker.
32756          * @param {Roo.bootstrap.LocationPicker} this
32757          * @param {Map event} e
32758          */
32759         markerClick : true,
32760         /**
32761          * @event markerRightClick
32762          * Fires when right click the marker.
32763          * @param {Roo.bootstrap.LocationPicker} this
32764          * @param {Map event} e
32765          */
32766         markerRightClick : true,
32767         /**
32768          * @event OverlayViewDraw
32769          * Fires when OverlayView Draw
32770          * @param {Roo.bootstrap.LocationPicker} this
32771          */
32772         OverlayViewDraw : true,
32773         /**
32774          * @event OverlayViewOnAdd
32775          * Fires when OverlayView Draw
32776          * @param {Roo.bootstrap.LocationPicker} this
32777          */
32778         OverlayViewOnAdd : true,
32779         /**
32780          * @event OverlayViewOnRemove
32781          * Fires when OverlayView Draw
32782          * @param {Roo.bootstrap.LocationPicker} this
32783          */
32784         OverlayViewOnRemove : true,
32785         /**
32786          * @event OverlayViewShow
32787          * Fires when OverlayView Draw
32788          * @param {Roo.bootstrap.LocationPicker} this
32789          * @param {Pixel} cpx
32790          */
32791         OverlayViewShow : true,
32792         /**
32793          * @event OverlayViewHide
32794          * Fires when OverlayView Draw
32795          * @param {Roo.bootstrap.LocationPicker} this
32796          */
32797         OverlayViewHide : true,
32798         /**
32799          * @event loadexception
32800          * Fires when load google lib failed.
32801          * @param {Roo.bootstrap.LocationPicker} this
32802          */
32803         loadexception : true
32804     });
32805         
32806 };
32807
32808 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
32809     
32810     gMapContext: false,
32811     
32812     latitude: 0,
32813     longitude: 0,
32814     zoom: 15,
32815     mapTypeId: false,
32816     mapTypeControl: false,
32817     disableDoubleClickZoom: false,
32818     scrollwheel: true,
32819     streetViewControl: false,
32820     radius: 0,
32821     locationName: '',
32822     draggable: true,
32823     enableAutocomplete: false,
32824     enableReverseGeocode: true,
32825     markerTitle: '',
32826     
32827     getAutoCreate: function()
32828     {
32829
32830         var cfg = {
32831             tag: 'div',
32832             cls: 'roo-location-picker'
32833         };
32834         
32835         return cfg
32836     },
32837     
32838     initEvents: function(ct, position)
32839     {       
32840         if(!this.el.getWidth() || this.isApplied()){
32841             return;
32842         }
32843         
32844         this.el.setVisibilityMode(Roo.Element.DISPLAY);
32845         
32846         this.initial();
32847     },
32848     
32849     initial: function()
32850     {
32851         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
32852             this.fireEvent('loadexception', this);
32853             return;
32854         }
32855         
32856         if(!this.mapTypeId){
32857             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
32858         }
32859         
32860         this.gMapContext = this.GMapContext();
32861         
32862         this.initOverlayView();
32863         
32864         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
32865         
32866         var _this = this;
32867                 
32868         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
32869             _this.setPosition(_this.gMapContext.marker.position);
32870         });
32871         
32872         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
32873             _this.fireEvent('mapClick', this, event);
32874             
32875         });
32876
32877         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
32878             _this.fireEvent('mapRightClick', this, event);
32879             
32880         });
32881         
32882         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
32883             _this.fireEvent('markerClick', this, event);
32884             
32885         });
32886
32887         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
32888             _this.fireEvent('markerRightClick', this, event);
32889             
32890         });
32891         
32892         this.setPosition(this.gMapContext.location);
32893         
32894         this.fireEvent('initial', this, this.gMapContext.location);
32895     },
32896     
32897     initOverlayView: function()
32898     {
32899         var _this = this;
32900         
32901         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
32902             
32903             draw: function()
32904             {
32905                 _this.fireEvent('OverlayViewDraw', _this);
32906             },
32907             
32908             onAdd: function()
32909             {
32910                 _this.fireEvent('OverlayViewOnAdd', _this);
32911             },
32912             
32913             onRemove: function()
32914             {
32915                 _this.fireEvent('OverlayViewOnRemove', _this);
32916             },
32917             
32918             show: function(cpx)
32919             {
32920                 _this.fireEvent('OverlayViewShow', _this, cpx);
32921             },
32922             
32923             hide: function()
32924             {
32925                 _this.fireEvent('OverlayViewHide', _this);
32926             }
32927             
32928         });
32929     },
32930     
32931     fromLatLngToContainerPixel: function(event)
32932     {
32933         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
32934     },
32935     
32936     isApplied: function() 
32937     {
32938         return this.getGmapContext() == false ? false : true;
32939     },
32940     
32941     getGmapContext: function() 
32942     {
32943         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
32944     },
32945     
32946     GMapContext: function() 
32947     {
32948         var position = new google.maps.LatLng(this.latitude, this.longitude);
32949         
32950         var _map = new google.maps.Map(this.el.dom, {
32951             center: position,
32952             zoom: this.zoom,
32953             mapTypeId: this.mapTypeId,
32954             mapTypeControl: this.mapTypeControl,
32955             disableDoubleClickZoom: this.disableDoubleClickZoom,
32956             scrollwheel: this.scrollwheel,
32957             streetViewControl: this.streetViewControl,
32958             locationName: this.locationName,
32959             draggable: this.draggable,
32960             enableAutocomplete: this.enableAutocomplete,
32961             enableReverseGeocode: this.enableReverseGeocode
32962         });
32963         
32964         var _marker = new google.maps.Marker({
32965             position: position,
32966             map: _map,
32967             title: this.markerTitle,
32968             draggable: this.draggable
32969         });
32970         
32971         return {
32972             map: _map,
32973             marker: _marker,
32974             circle: null,
32975             location: position,
32976             radius: this.radius,
32977             locationName: this.locationName,
32978             addressComponents: {
32979                 formatted_address: null,
32980                 addressLine1: null,
32981                 addressLine2: null,
32982                 streetName: null,
32983                 streetNumber: null,
32984                 city: null,
32985                 district: null,
32986                 state: null,
32987                 stateOrProvince: null
32988             },
32989             settings: this,
32990             domContainer: this.el.dom,
32991             geodecoder: new google.maps.Geocoder()
32992         };
32993     },
32994     
32995     drawCircle: function(center, radius, options) 
32996     {
32997         if (this.gMapContext.circle != null) {
32998             this.gMapContext.circle.setMap(null);
32999         }
33000         if (radius > 0) {
33001             radius *= 1;
33002             options = Roo.apply({}, options, {
33003                 strokeColor: "#0000FF",
33004                 strokeOpacity: .35,
33005                 strokeWeight: 2,
33006                 fillColor: "#0000FF",
33007                 fillOpacity: .2
33008             });
33009             
33010             options.map = this.gMapContext.map;
33011             options.radius = radius;
33012             options.center = center;
33013             this.gMapContext.circle = new google.maps.Circle(options);
33014             return this.gMapContext.circle;
33015         }
33016         
33017         return null;
33018     },
33019     
33020     setPosition: function(location) 
33021     {
33022         this.gMapContext.location = location;
33023         this.gMapContext.marker.setPosition(location);
33024         this.gMapContext.map.panTo(location);
33025         this.drawCircle(location, this.gMapContext.radius, {});
33026         
33027         var _this = this;
33028         
33029         if (this.gMapContext.settings.enableReverseGeocode) {
33030             this.gMapContext.geodecoder.geocode({
33031                 latLng: this.gMapContext.location
33032             }, function(results, status) {
33033                 
33034                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
33035                     _this.gMapContext.locationName = results[0].formatted_address;
33036                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
33037                     
33038                     _this.fireEvent('positionchanged', this, location);
33039                 }
33040             });
33041             
33042             return;
33043         }
33044         
33045         this.fireEvent('positionchanged', this, location);
33046     },
33047     
33048     resize: function()
33049     {
33050         google.maps.event.trigger(this.gMapContext.map, "resize");
33051         
33052         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
33053         
33054         this.fireEvent('resize', this);
33055     },
33056     
33057     setPositionByLatLng: function(latitude, longitude)
33058     {
33059         this.setPosition(new google.maps.LatLng(latitude, longitude));
33060     },
33061     
33062     getCurrentPosition: function() 
33063     {
33064         return {
33065             latitude: this.gMapContext.location.lat(),
33066             longitude: this.gMapContext.location.lng()
33067         };
33068     },
33069     
33070     getAddressName: function() 
33071     {
33072         return this.gMapContext.locationName;
33073     },
33074     
33075     getAddressComponents: function() 
33076     {
33077         return this.gMapContext.addressComponents;
33078     },
33079     
33080     address_component_from_google_geocode: function(address_components) 
33081     {
33082         var result = {};
33083         
33084         for (var i = 0; i < address_components.length; i++) {
33085             var component = address_components[i];
33086             if (component.types.indexOf("postal_code") >= 0) {
33087                 result.postalCode = component.short_name;
33088             } else if (component.types.indexOf("street_number") >= 0) {
33089                 result.streetNumber = component.short_name;
33090             } else if (component.types.indexOf("route") >= 0) {
33091                 result.streetName = component.short_name;
33092             } else if (component.types.indexOf("neighborhood") >= 0) {
33093                 result.city = component.short_name;
33094             } else if (component.types.indexOf("locality") >= 0) {
33095                 result.city = component.short_name;
33096             } else if (component.types.indexOf("sublocality") >= 0) {
33097                 result.district = component.short_name;
33098             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
33099                 result.stateOrProvince = component.short_name;
33100             } else if (component.types.indexOf("country") >= 0) {
33101                 result.country = component.short_name;
33102             }
33103         }
33104         
33105         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
33106         result.addressLine2 = "";
33107         return result;
33108     },
33109     
33110     setZoomLevel: function(zoom)
33111     {
33112         this.gMapContext.map.setZoom(zoom);
33113     },
33114     
33115     show: function()
33116     {
33117         if(!this.el){
33118             return;
33119         }
33120         
33121         this.el.show();
33122         
33123         this.resize();
33124         
33125         this.fireEvent('show', this);
33126     },
33127     
33128     hide: function()
33129     {
33130         if(!this.el){
33131             return;
33132         }
33133         
33134         this.el.hide();
33135         
33136         this.fireEvent('hide', this);
33137     }
33138     
33139 });
33140
33141 Roo.apply(Roo.bootstrap.LocationPicker, {
33142     
33143     OverlayView : function(map, options)
33144     {
33145         options = options || {};
33146         
33147         this.setMap(map);
33148     }
33149     
33150     
33151 });/**
33152  * @class Roo.bootstrap.Alert
33153  * @extends Roo.bootstrap.Component
33154  * Bootstrap Alert class - shows an alert area box
33155  * eg
33156  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
33157   Enter a valid email address
33158 </div>
33159  * @licence LGPL
33160  * @cfg {String} title The title of alert
33161  * @cfg {String} html The content of alert
33162  * @cfg {String} weight (success|info|warning|danger) Weight of the message
33163  * @cfg {String} fa font-awesomeicon
33164  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
33165  * @cfg {Boolean} close true to show a x closer
33166  * 
33167  * 
33168  * @constructor
33169  * Create a new alert
33170  * @param {Object} config The config object
33171  */
33172
33173
33174 Roo.bootstrap.Alert = function(config){
33175     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
33176     
33177 };
33178
33179 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
33180     
33181     title: '',
33182     html: '',
33183     weight: false,
33184     fa: false,
33185     faicon: false, // BC
33186     close : false,
33187     
33188     
33189     getAutoCreate : function()
33190     {
33191         
33192         var cfg = {
33193             tag : 'div',
33194             cls : 'alert',
33195             cn : [
33196                 {
33197                     tag: 'button',
33198                     type :  "button",
33199                     cls: "close",
33200                     html : '×',
33201                     style : this.close ? '' : 'display:none'
33202                 },
33203                 {
33204                     tag : 'i',
33205                     cls : 'roo-alert-icon'
33206                     
33207                 },
33208                 {
33209                     tag : 'b',
33210                     cls : 'roo-alert-title',
33211                     html : this.title
33212                 },
33213                 {
33214                     tag : 'span',
33215                     cls : 'roo-alert-text',
33216                     html : this.html
33217                 }
33218             ]
33219         };
33220         
33221         if(this.faicon){
33222             cfg.cn[0].cls += ' fa ' + this.faicon;
33223         }
33224         if(this.fa){
33225             cfg.cn[0].cls += ' fa ' + this.fa;
33226         }
33227         
33228         if(this.weight){
33229             cfg.cls += ' alert-' + this.weight;
33230         }
33231         
33232         return cfg;
33233     },
33234     
33235     initEvents: function() 
33236     {
33237         this.el.setVisibilityMode(Roo.Element.DISPLAY);
33238         this.titleEl =  this.el.select('.roo-alert-title',true).first();
33239         this.iconEl = this.el.select('.roo-alert-icon',true).first();
33240         this.htmlEl = this.el.select('.roo-alert-text',true).first();
33241         if (this.seconds > 0) {
33242             this.hide.defer(this.seconds, this);
33243         }
33244     },
33245     /**
33246      * Set the Title Message HTML
33247      * @param {String} html
33248      */
33249     setTitle : function(str)
33250     {
33251         this.titleEl.dom.innerHTML = str;
33252     },
33253      
33254      /**
33255      * Set the Body Message HTML
33256      * @param {String} html
33257      */
33258     setHtml : function(str)
33259     {
33260         this.htmlEl.dom.innerHTML = str;
33261     },
33262     /**
33263      * Set the Weight of the alert
33264      * @param {String} (success|info|warning|danger) weight
33265      */
33266     
33267     setWeight : function(weight)
33268     {
33269         if(this.weight){
33270             this.el.removeClass('alert-' + this.weight);
33271         }
33272         
33273         this.weight = weight;
33274         
33275         this.el.addClass('alert-' + this.weight);
33276     },
33277       /**
33278      * Set the Icon of the alert
33279      * @param {String} see fontawsome names (name without the 'fa-' bit)
33280      */
33281     setIcon : function(icon)
33282     {
33283         if(this.faicon){
33284             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
33285         }
33286         
33287         this.faicon = icon;
33288         
33289         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
33290     },
33291     /**
33292      * Hide the Alert
33293      */
33294     hide: function() 
33295     {
33296         this.el.hide();   
33297     },
33298     /**
33299      * Show the Alert
33300      */
33301     show: function() 
33302     {  
33303         this.el.show();   
33304     }
33305     
33306 });
33307
33308  
33309 /*
33310 * Licence: LGPL
33311 */
33312
33313 /**
33314  * @class Roo.bootstrap.UploadCropbox
33315  * @extends Roo.bootstrap.Component
33316  * Bootstrap UploadCropbox class
33317  * @cfg {String} emptyText show when image has been loaded
33318  * @cfg {String} rotateNotify show when image too small to rotate
33319  * @cfg {Number} errorTimeout default 3000
33320  * @cfg {Number} minWidth default 300
33321  * @cfg {Number} minHeight default 300
33322  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
33323  * @cfg {Boolean} isDocument (true|false) default false
33324  * @cfg {String} url action url
33325  * @cfg {String} paramName default 'imageUpload'
33326  * @cfg {String} method default POST
33327  * @cfg {Boolean} loadMask (true|false) default true
33328  * @cfg {Boolean} loadingText default 'Loading...'
33329  * 
33330  * @constructor
33331  * Create a new UploadCropbox
33332  * @param {Object} config The config object
33333  */
33334
33335 Roo.bootstrap.UploadCropbox = function(config){
33336     console.log("BOOTSTRAP UPLOAD CROPBOX CONSTRUCTOR");
33337     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
33338     
33339     this.addEvents({
33340         /**
33341          * @event beforeselectfile
33342          * Fire before select file
33343          * @param {Roo.bootstrap.UploadCropbox} this
33344          */
33345         "beforeselectfile" : true,
33346         /**
33347          * @event initial
33348          * Fire after initEvent
33349          * @param {Roo.bootstrap.UploadCropbox} this
33350          */
33351         "initial" : true,
33352         /**
33353          * @event crop
33354          * Fire after initEvent
33355          * @param {Roo.bootstrap.UploadCropbox} this
33356          * @param {String} data
33357          */
33358         "crop" : true,
33359         /**
33360          * @event prepare
33361          * Fire when preparing the file data
33362          * @param {Roo.bootstrap.UploadCropbox} this
33363          * @param {Object} file
33364          */
33365         "prepare" : true,
33366         /**
33367          * @event exception
33368          * Fire when get exception
33369          * @param {Roo.bootstrap.UploadCropbox} this
33370          * @param {XMLHttpRequest} xhr
33371          */
33372         "exception" : true,
33373         /**
33374          * @event beforeloadcanvas
33375          * Fire before load the canvas
33376          * @param {Roo.bootstrap.UploadCropbox} this
33377          * @param {String} src
33378          */
33379         "beforeloadcanvas" : true,
33380         /**
33381          * @event trash
33382          * Fire when trash image
33383          * @param {Roo.bootstrap.UploadCropbox} this
33384          */
33385         "trash" : true,
33386         /**
33387          * @event download
33388          * Fire when download the image
33389          * @param {Roo.bootstrap.UploadCropbox} this
33390          */
33391         "download" : true,
33392         /**
33393          * @event footerbuttonclick
33394          * Fire when footerbuttonclick
33395          * @param {Roo.bootstrap.UploadCropbox} this
33396          * @param {String} type
33397          */
33398         "footerbuttonclick" : true,
33399         /**
33400          * @event resize
33401          * Fire when resize
33402          * @param {Roo.bootstrap.UploadCropbox} this
33403          */
33404         "resize" : true,
33405         /**
33406          * @event rotate
33407          * Fire when rotate the image
33408          * @param {Roo.bootstrap.UploadCropbox} this
33409          * @param {String} pos
33410          */
33411         "rotate" : true,
33412         /**
33413          * @event inspect
33414          * Fire when inspect the file
33415          * @param {Roo.bootstrap.UploadCropbox} this
33416          * @param {Object} file
33417          */
33418         "inspect" : true,
33419         /**
33420          * @event upload
33421          * Fire when xhr upload the file
33422          * @param {Roo.bootstrap.UploadCropbox} this
33423          * @param {Object} data
33424          */
33425         "upload" : true,
33426         /**
33427          * @event arrange
33428          * Fire when arrange the file data
33429          * @param {Roo.bootstrap.UploadCropbox} this
33430          * @param {Object} formData
33431          */
33432         "arrange" : true
33433     });
33434     
33435     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
33436 };
33437
33438 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
33439     
33440     emptyText : 'Click to upload image',
33441     rotateNotify : 'Image is too small to rotate',
33442     errorTimeout : 3000,
33443     scale : 0,
33444     baseScale : 1,
33445     rotate : 0,
33446     dragable : false,
33447     pinching : false,
33448     mouseX : 0,
33449     mouseY : 0,
33450     cropData : false,
33451     minWidth : 300,
33452     minHeight : 300,
33453     file : false,
33454     exif : {},
33455     baseRotate : 1,
33456     cropType : 'image/jpeg',
33457     buttons : false,
33458     canvasLoaded : false,
33459     isDocument : false,
33460     method : 'POST',
33461     paramName : 'imageUpload',
33462     loadMask : true,
33463     loadingText : 'Loading...',
33464     maskEl : false,
33465     
33466     getAutoCreate : function()
33467     {
33468         var cfg = {
33469             tag : 'div',
33470             cls : 'roo-upload-cropbox',
33471             cn : [
33472                 {
33473                     tag : 'input',
33474                     cls : 'roo-upload-cropbox-selector',
33475                     type : 'file'
33476                 },
33477                 {
33478                     tag : 'div',
33479                     cls : 'roo-upload-cropbox-body',
33480                     style : 'cursor:pointer',
33481                     cn : [
33482                         {
33483                             tag : 'div',
33484                             cls : 'roo-upload-cropbox-preview'
33485                         },
33486                         {
33487                             tag : 'div',
33488                             cls : 'roo-upload-cropbox-thumb'
33489                         },
33490                         {
33491                             tag : 'div',
33492                             cls : 'roo-upload-cropbox-empty-notify',
33493                             html : this.emptyText
33494                         },
33495                         {
33496                             tag : 'div',
33497                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
33498                             html : this.rotateNotify
33499                         }
33500                     ]
33501                 },
33502                 {
33503                     tag : 'div',
33504                     cls : 'roo-upload-cropbox-footer',
33505                     cn : {
33506                         tag : 'div',
33507                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
33508                         cn : []
33509                     }
33510                 }
33511             ]
33512         };
33513         
33514         return cfg;
33515     },
33516     
33517     onRender : function(ct, position)
33518     {
33519         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
33520         
33521         if (this.buttons.length) {
33522             
33523             Roo.each(this.buttons, function(bb) {
33524                 
33525                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
33526                 
33527                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
33528                 
33529             }, this);
33530         }
33531         
33532         if(this.loadMask){
33533             this.maskEl = this.el;
33534         }
33535     },
33536     
33537     initEvents : function()
33538     {
33539         this.urlAPI = (window.createObjectURL && window) || 
33540                                 (window.URL && URL.revokeObjectURL && URL) || 
33541                                 (window.webkitURL && webkitURL);
33542                         
33543         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
33544         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33545         
33546         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
33547         this.selectorEl.hide();
33548         
33549         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
33550         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33551         
33552         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
33553         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33554         this.thumbEl.hide();
33555         
33556         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
33557         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33558         
33559         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
33560         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33561         this.errorEl.hide();
33562         
33563         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
33564         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33565         this.footerEl.hide();
33566         
33567         this.setThumbBoxSize();
33568         
33569         this.bind();
33570         
33571         this.resize();
33572         
33573         this.fireEvent('initial', this);
33574     },
33575
33576     bind : function()
33577     {
33578         var _this = this;
33579         
33580         window.addEventListener("resize", function() { _this.resize(); } );
33581         
33582         this.bodyEl.on('click', this.beforeSelectFile, this);
33583         
33584         if(Roo.isTouch){
33585             this.bodyEl.on('touchstart', this.onTouchStart, this);
33586             this.bodyEl.on('touchmove', this.onTouchMove, this);
33587             this.bodyEl.on('touchend', this.onTouchEnd, this);
33588         }
33589         
33590         if(!Roo.isTouch){
33591             this.bodyEl.on('mousedown', this.onMouseDown, this);
33592             this.bodyEl.on('mousemove', this.onMouseMove, this);
33593             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
33594             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
33595             Roo.get(document).on('mouseup', this.onMouseUp, this);
33596         }
33597         
33598         this.selectorEl.on('change', this.onFileSelected, this);
33599     },
33600     
33601     reset : function()
33602     {    
33603         this.scale = 0;
33604         this.baseScale = 1;
33605         this.rotate = 0;
33606         this.baseRotate = 1;
33607         this.dragable = false;
33608         this.pinching = false;
33609         this.mouseX = 0;
33610         this.mouseY = 0;
33611         this.cropData = false;
33612         this.notifyEl.dom.innerHTML = this.emptyText;
33613         
33614         this.selectorEl.dom.value = '';
33615         
33616     },
33617     
33618     resize : function()
33619     {
33620         if(this.fireEvent('resize', this) != false){
33621             this.setThumbBoxPosition();
33622             this.setCanvasPosition();
33623         }
33624     },
33625     
33626     onFooterButtonClick : function(e, el, o, type)
33627     {
33628         switch (type) {
33629             case 'rotate-left' :
33630                 this.onRotateLeft(e);
33631                 break;
33632             case 'rotate-right' :
33633                 this.onRotateRight(e);
33634                 break;
33635             case 'picture' :
33636                 this.beforeSelectFile(e);
33637                 break;
33638             case 'trash' :
33639                 this.trash(e);
33640                 break;
33641             case 'crop' :
33642                 this.crop(e);
33643                 break;
33644             case 'download' :
33645                 this.download(e);
33646                 break;
33647             default :
33648                 break;
33649         }
33650         
33651         this.fireEvent('footerbuttonclick', this, type);
33652     },
33653     
33654     beforeSelectFile : function(e)
33655     {
33656         e.preventDefault();
33657         
33658         if(this.fireEvent('beforeselectfile', this) != false){
33659             this.selectorEl.dom.click();
33660         }
33661     },
33662     
33663     onFileSelected : function(e)
33664     {
33665         e.preventDefault();
33666         
33667         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
33668             return;
33669         }
33670         
33671         var file = this.selectorEl.dom.files[0];
33672         
33673         if(this.fireEvent('inspect', this, file) != false){
33674             this.prepare(file);
33675         }
33676         
33677     },
33678     
33679     trash : function(e)
33680     {
33681         this.fireEvent('trash', this);
33682     },
33683     
33684     download : function(e)
33685     {
33686         this.fireEvent('download', this);
33687     },
33688     
33689     loadCanvas : function(src)
33690     {   
33691         if(this.fireEvent('beforeloadcanvas', this, src) != false){
33692             
33693             this.reset();
33694             
33695             this.imageEl = document.createElement('img');
33696             
33697             var _this = this;
33698             
33699             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
33700             
33701             this.imageEl.src = src;
33702         }
33703     },
33704     
33705     onLoadCanvas : function()
33706     {   
33707         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
33708         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
33709         
33710         this.bodyEl.un('click', this.beforeSelectFile, this);
33711         
33712         this.notifyEl.hide();
33713         this.thumbEl.show();
33714         this.footerEl.show();
33715         
33716         this.baseRotateLevel();
33717         
33718         if(this.isDocument){
33719             this.setThumbBoxSize();
33720         }
33721         
33722         this.setThumbBoxPosition();
33723         
33724         this.baseScaleLevel();
33725         
33726         this.draw();
33727         
33728         this.resize();
33729         
33730         this.canvasLoaded = true;
33731         
33732         if(this.loadMask){
33733             this.maskEl.unmask();
33734         }
33735         
33736     },
33737     
33738     setCanvasPosition : function()
33739     {   
33740         if(!this.canvasEl){
33741             return;
33742         }
33743         
33744         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
33745         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
33746         
33747         this.previewEl.setLeft(pw);
33748         this.previewEl.setTop(ph);
33749         
33750     },
33751     
33752     onMouseDown : function(e)
33753     {   
33754         e.stopEvent();
33755         
33756         this.dragable = true;
33757         this.pinching = false;
33758         
33759         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
33760             this.dragable = false;
33761             return;
33762         }
33763         
33764         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33765         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33766         
33767     },
33768     
33769     onMouseMove : function(e)
33770     {   
33771         e.stopEvent();
33772         
33773         if(!this.canvasLoaded){
33774             return;
33775         }
33776         
33777         if (!this.dragable){
33778             return;
33779         }
33780         
33781         var minX = Math.ceil(this.thumbEl.getLeft(true));
33782         var minY = Math.ceil(this.thumbEl.getTop(true));
33783         
33784         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
33785         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
33786         
33787         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33788         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33789         
33790         x = x - this.mouseX;
33791         y = y - this.mouseY;
33792         
33793         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
33794         var bgY = Math.ceil(y + this.previewEl.getTop(true));
33795         
33796         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
33797         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
33798         
33799         this.previewEl.setLeft(bgX);
33800         this.previewEl.setTop(bgY);
33801         
33802         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33803         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33804     },
33805     
33806     onMouseUp : function(e)
33807     {   
33808         e.stopEvent();
33809         
33810         this.dragable = false;
33811     },
33812     
33813     onMouseWheel : function(e)
33814     {   
33815         e.stopEvent();
33816         
33817         this.startScale = this.scale;
33818         
33819         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
33820         
33821         if(!this.zoomable()){
33822             this.scale = this.startScale;
33823             return;
33824         }
33825         
33826         this.draw();
33827         
33828         return;
33829     },
33830     
33831     zoomable : function()
33832     {
33833         var minScale = this.thumbEl.getWidth() / this.minWidth;
33834         
33835         if(this.minWidth < this.minHeight){
33836             minScale = this.thumbEl.getHeight() / this.minHeight;
33837         }
33838         
33839         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
33840         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
33841         
33842         if(
33843                 this.isDocument &&
33844                 (this.rotate == 0 || this.rotate == 180) && 
33845                 (
33846                     width > this.imageEl.OriginWidth || 
33847                     height > this.imageEl.OriginHeight ||
33848                     (width < this.minWidth && height < this.minHeight)
33849                 )
33850         ){
33851             return false;
33852         }
33853         
33854         if(
33855                 this.isDocument &&
33856                 (this.rotate == 90 || this.rotate == 270) && 
33857                 (
33858                     width > this.imageEl.OriginWidth || 
33859                     height > this.imageEl.OriginHeight ||
33860                     (width < this.minHeight && height < this.minWidth)
33861                 )
33862         ){
33863             return false;
33864         }
33865         
33866         if(
33867                 !this.isDocument &&
33868                 (this.rotate == 0 || this.rotate == 180) && 
33869                 (
33870                     width < this.minWidth || 
33871                     width > this.imageEl.OriginWidth || 
33872                     height < this.minHeight || 
33873                     height > this.imageEl.OriginHeight
33874                 )
33875         ){
33876             return false;
33877         }
33878         
33879         if(
33880                 !this.isDocument &&
33881                 (this.rotate == 90 || this.rotate == 270) && 
33882                 (
33883                     width < this.minHeight || 
33884                     width > this.imageEl.OriginWidth || 
33885                     height < this.minWidth || 
33886                     height > this.imageEl.OriginHeight
33887                 )
33888         ){
33889             return false;
33890         }
33891         
33892         return true;
33893         
33894     },
33895     
33896     onRotateLeft : function(e)
33897     {   
33898         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
33899             
33900             var minScale = this.thumbEl.getWidth() / this.minWidth;
33901             
33902             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
33903             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
33904             
33905             this.startScale = this.scale;
33906             
33907             while (this.getScaleLevel() < minScale){
33908             
33909                 this.scale = this.scale + 1;
33910                 
33911                 if(!this.zoomable()){
33912                     break;
33913                 }
33914                 
33915                 if(
33916                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
33917                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
33918                 ){
33919                     continue;
33920                 }
33921                 
33922                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
33923
33924                 this.draw();
33925                 
33926                 return;
33927             }
33928             
33929             this.scale = this.startScale;
33930             
33931             this.onRotateFail();
33932             
33933             return false;
33934         }
33935         
33936         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
33937
33938         if(this.isDocument){
33939             this.setThumbBoxSize();
33940             this.setThumbBoxPosition();
33941             this.setCanvasPosition();
33942         }
33943         
33944         this.draw();
33945         
33946         this.fireEvent('rotate', this, 'left');
33947         
33948     },
33949     
33950     onRotateRight : function(e)
33951     {
33952         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
33953             
33954             var minScale = this.thumbEl.getWidth() / this.minWidth;
33955         
33956             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
33957             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
33958             
33959             this.startScale = this.scale;
33960             
33961             while (this.getScaleLevel() < minScale){
33962             
33963                 this.scale = this.scale + 1;
33964                 
33965                 if(!this.zoomable()){
33966                     break;
33967                 }
33968                 
33969                 if(
33970                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
33971                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
33972                 ){
33973                     continue;
33974                 }
33975                 
33976                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
33977
33978                 this.draw();
33979                 
33980                 return;
33981             }
33982             
33983             this.scale = this.startScale;
33984             
33985             this.onRotateFail();
33986             
33987             return false;
33988         }
33989         
33990         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
33991
33992         if(this.isDocument){
33993             this.setThumbBoxSize();
33994             this.setThumbBoxPosition();
33995             this.setCanvasPosition();
33996         }
33997         
33998         this.draw();
33999         
34000         this.fireEvent('rotate', this, 'right');
34001     },
34002     
34003     onRotateFail : function()
34004     {
34005         this.errorEl.show(true);
34006         
34007         var _this = this;
34008         
34009         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
34010     },
34011     
34012     draw : function()
34013     {
34014         this.previewEl.dom.innerHTML = '';
34015         
34016         var canvasEl = document.createElement("canvas");
34017         
34018         var contextEl = canvasEl.getContext("2d");
34019         
34020         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34021         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34022         var center = this.imageEl.OriginWidth / 2;
34023         
34024         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
34025             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34026             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34027             center = this.imageEl.OriginHeight / 2;
34028         }
34029         
34030         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
34031         
34032         contextEl.translate(center, center);
34033         contextEl.rotate(this.rotate * Math.PI / 180);
34034
34035         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34036         
34037         this.canvasEl = document.createElement("canvas");
34038         
34039         this.contextEl = this.canvasEl.getContext("2d");
34040         
34041         switch (this.rotate) {
34042             case 0 :
34043                 
34044                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34045                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34046                 
34047                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34048                 
34049                 break;
34050             case 90 : 
34051                 
34052                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34053                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34054                 
34055                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34056                     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);
34057                     break;
34058                 }
34059                 
34060                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34061                 
34062                 break;
34063             case 180 :
34064                 
34065                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34066                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34067                 
34068                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34069                     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);
34070                     break;
34071                 }
34072                 
34073                 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);
34074                 
34075                 break;
34076             case 270 :
34077                 
34078                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34079                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34080         
34081                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34082                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34083                     break;
34084                 }
34085                 
34086                 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);
34087                 
34088                 break;
34089             default : 
34090                 break;
34091         }
34092         
34093         this.previewEl.appendChild(this.canvasEl);
34094         
34095         this.setCanvasPosition();
34096     },
34097     
34098     crop : function()
34099     {
34100         if(!this.canvasLoaded){
34101             return;
34102         }
34103         
34104         var imageCanvas = document.createElement("canvas");
34105         
34106         var imageContext = imageCanvas.getContext("2d");
34107         
34108         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34109         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34110         
34111         var center = imageCanvas.width / 2;
34112         
34113         imageContext.translate(center, center);
34114         
34115         imageContext.rotate(this.rotate * Math.PI / 180);
34116         
34117         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34118         
34119         var canvas = document.createElement("canvas");
34120         
34121         var context = canvas.getContext("2d");
34122                 
34123         canvas.width = this.minWidth;
34124         canvas.height = this.minHeight;
34125
34126         switch (this.rotate) {
34127             case 0 :
34128                 
34129                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34130                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34131                 
34132                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34133                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34134                 
34135                 var targetWidth = this.minWidth - 2 * x;
34136                 var targetHeight = this.minHeight - 2 * y;
34137                 
34138                 var scale = 1;
34139                 
34140                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34141                     scale = targetWidth / width;
34142                 }
34143                 
34144                 if(x > 0 && y == 0){
34145                     scale = targetHeight / height;
34146                 }
34147                 
34148                 if(x > 0 && y > 0){
34149                     scale = targetWidth / width;
34150                     
34151                     if(width < height){
34152                         scale = targetHeight / height;
34153                     }
34154                 }
34155                 
34156                 context.scale(scale, scale);
34157                 
34158                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34159                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34160
34161                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34162                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34163
34164                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34165                 
34166                 break;
34167             case 90 : 
34168                 
34169                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34170                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34171                 
34172                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34173                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34174                 
34175                 var targetWidth = this.minWidth - 2 * x;
34176                 var targetHeight = this.minHeight - 2 * y;
34177                 
34178                 var scale = 1;
34179                 
34180                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34181                     scale = targetWidth / width;
34182                 }
34183                 
34184                 if(x > 0 && y == 0){
34185                     scale = targetHeight / height;
34186                 }
34187                 
34188                 if(x > 0 && y > 0){
34189                     scale = targetWidth / width;
34190                     
34191                     if(width < height){
34192                         scale = targetHeight / height;
34193                     }
34194                 }
34195                 
34196                 context.scale(scale, scale);
34197                 
34198                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34199                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34200
34201                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34202                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34203                 
34204                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34205                 
34206                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34207                 
34208                 break;
34209             case 180 :
34210                 
34211                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34212                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34213                 
34214                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34215                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34216                 
34217                 var targetWidth = this.minWidth - 2 * x;
34218                 var targetHeight = this.minHeight - 2 * y;
34219                 
34220                 var scale = 1;
34221                 
34222                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34223                     scale = targetWidth / width;
34224                 }
34225                 
34226                 if(x > 0 && y == 0){
34227                     scale = targetHeight / height;
34228                 }
34229                 
34230                 if(x > 0 && y > 0){
34231                     scale = targetWidth / width;
34232                     
34233                     if(width < height){
34234                         scale = targetHeight / height;
34235                     }
34236                 }
34237                 
34238                 context.scale(scale, scale);
34239                 
34240                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34241                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34242
34243                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34244                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34245
34246                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34247                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34248                 
34249                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34250                 
34251                 break;
34252             case 270 :
34253                 
34254                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34255                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34256                 
34257                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34258                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34259                 
34260                 var targetWidth = this.minWidth - 2 * x;
34261                 var targetHeight = this.minHeight - 2 * y;
34262                 
34263                 var scale = 1;
34264                 
34265                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34266                     scale = targetWidth / width;
34267                 }
34268                 
34269                 if(x > 0 && y == 0){
34270                     scale = targetHeight / height;
34271                 }
34272                 
34273                 if(x > 0 && y > 0){
34274                     scale = targetWidth / width;
34275                     
34276                     if(width < height){
34277                         scale = targetHeight / height;
34278                     }
34279                 }
34280                 
34281                 context.scale(scale, scale);
34282                 
34283                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34284                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34285
34286                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34287                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34288                 
34289                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34290                 
34291                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34292                 
34293                 break;
34294             default : 
34295                 break;
34296         }
34297         
34298         this.cropData = canvas.toDataURL(this.cropType);
34299         
34300         if(this.fireEvent('crop', this, this.cropData) !== false){
34301             this.process(this.file, this.cropData);
34302         }
34303         
34304         return;
34305         
34306     },
34307     
34308     setThumbBoxSize : function()
34309     {
34310         var width, height;
34311         
34312         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
34313             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
34314             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
34315             
34316             this.minWidth = width;
34317             this.minHeight = height;
34318             
34319             if(this.rotate == 90 || this.rotate == 270){
34320                 this.minWidth = height;
34321                 this.minHeight = width;
34322             }
34323         }
34324         
34325         height = 300;
34326         width = Math.ceil(this.minWidth * height / this.minHeight);
34327         
34328         if(this.minWidth > this.minHeight){
34329             width = 300;
34330             height = Math.ceil(this.minHeight * width / this.minWidth);
34331         }
34332         
34333         this.thumbEl.setStyle({
34334             width : width + 'px',
34335             height : height + 'px'
34336         });
34337
34338         return;
34339             
34340     },
34341     
34342     setThumbBoxPosition : function()
34343     {
34344         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
34345         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
34346         
34347         this.thumbEl.setLeft(x);
34348         this.thumbEl.setTop(y);
34349         
34350     },
34351     
34352     baseRotateLevel : function()
34353     {
34354         this.baseRotate = 1;
34355         
34356         if(
34357                 typeof(this.exif) != 'undefined' &&
34358                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
34359                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
34360         ){
34361             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
34362         }
34363         
34364         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
34365         
34366     },
34367     
34368     baseScaleLevel : function()
34369     {
34370         var width, height;
34371         
34372         if(this.isDocument){
34373             
34374             if(this.baseRotate == 6 || this.baseRotate == 8){
34375             
34376                 height = this.thumbEl.getHeight();
34377                 this.baseScale = height / this.imageEl.OriginWidth;
34378
34379                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
34380                     width = this.thumbEl.getWidth();
34381                     this.baseScale = width / this.imageEl.OriginHeight;
34382                 }
34383
34384                 return;
34385             }
34386
34387             height = this.thumbEl.getHeight();
34388             this.baseScale = height / this.imageEl.OriginHeight;
34389
34390             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
34391                 width = this.thumbEl.getWidth();
34392                 this.baseScale = width / this.imageEl.OriginWidth;
34393             }
34394
34395             return;
34396         }
34397         
34398         if(this.baseRotate == 6 || this.baseRotate == 8){
34399             
34400             width = this.thumbEl.getHeight();
34401             this.baseScale = width / this.imageEl.OriginHeight;
34402             
34403             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
34404                 height = this.thumbEl.getWidth();
34405                 this.baseScale = height / this.imageEl.OriginHeight;
34406             }
34407             
34408             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34409                 height = this.thumbEl.getWidth();
34410                 this.baseScale = height / this.imageEl.OriginHeight;
34411                 
34412                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
34413                     width = this.thumbEl.getHeight();
34414                     this.baseScale = width / this.imageEl.OriginWidth;
34415                 }
34416             }
34417             
34418             return;
34419         }
34420         
34421         width = this.thumbEl.getWidth();
34422         this.baseScale = width / this.imageEl.OriginWidth;
34423         
34424         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
34425             height = this.thumbEl.getHeight();
34426             this.baseScale = height / this.imageEl.OriginHeight;
34427         }
34428         
34429         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34430             
34431             height = this.thumbEl.getHeight();
34432             this.baseScale = height / this.imageEl.OriginHeight;
34433             
34434             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
34435                 width = this.thumbEl.getWidth();
34436                 this.baseScale = width / this.imageEl.OriginWidth;
34437             }
34438             
34439         }
34440         
34441         return;
34442     },
34443     
34444     getScaleLevel : function()
34445     {
34446         return this.baseScale * Math.pow(1.1, this.scale);
34447     },
34448     
34449     onTouchStart : function(e)
34450     {
34451         if(!this.canvasLoaded){
34452             this.beforeSelectFile(e);
34453             return;
34454         }
34455         
34456         var touches = e.browserEvent.touches;
34457         
34458         if(!touches){
34459             return;
34460         }
34461         
34462         if(touches.length == 1){
34463             this.onMouseDown(e);
34464             return;
34465         }
34466         
34467         if(touches.length != 2){
34468             return;
34469         }
34470         
34471         var coords = [];
34472         
34473         for(var i = 0, finger; finger = touches[i]; i++){
34474             coords.push(finger.pageX, finger.pageY);
34475         }
34476         
34477         var x = Math.pow(coords[0] - coords[2], 2);
34478         var y = Math.pow(coords[1] - coords[3], 2);
34479         
34480         this.startDistance = Math.sqrt(x + y);
34481         
34482         this.startScale = this.scale;
34483         
34484         this.pinching = true;
34485         this.dragable = false;
34486         
34487     },
34488     
34489     onTouchMove : function(e)
34490     {
34491         if(!this.pinching && !this.dragable){
34492             return;
34493         }
34494         
34495         var touches = e.browserEvent.touches;
34496         
34497         if(!touches){
34498             return;
34499         }
34500         
34501         if(this.dragable){
34502             this.onMouseMove(e);
34503             return;
34504         }
34505         
34506         var coords = [];
34507         
34508         for(var i = 0, finger; finger = touches[i]; i++){
34509             coords.push(finger.pageX, finger.pageY);
34510         }
34511         
34512         var x = Math.pow(coords[0] - coords[2], 2);
34513         var y = Math.pow(coords[1] - coords[3], 2);
34514         
34515         this.endDistance = Math.sqrt(x + y);
34516         
34517         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
34518         
34519         if(!this.zoomable()){
34520             this.scale = this.startScale;
34521             return;
34522         }
34523         
34524         this.draw();
34525         
34526     },
34527     
34528     onTouchEnd : function(e)
34529     {
34530         this.pinching = false;
34531         this.dragable = false;
34532         
34533     },
34534     
34535     process : function(file, crop)
34536     {
34537         if(this.loadMask){
34538             this.maskEl.mask(this.loadingText);
34539         }
34540         
34541         this.xhr = new XMLHttpRequest();
34542         
34543         file.xhr = this.xhr;
34544
34545         this.xhr.open(this.method, this.url, true);
34546         
34547         var headers = {
34548             "Accept": "application/json",
34549             "Cache-Control": "no-cache",
34550             "X-Requested-With": "XMLHttpRequest"
34551         };
34552         
34553         for (var headerName in headers) {
34554             var headerValue = headers[headerName];
34555             if (headerValue) {
34556                 this.xhr.setRequestHeader(headerName, headerValue);
34557             }
34558         }
34559         
34560         var _this = this;
34561         
34562         this.xhr.onload = function()
34563         {
34564             _this.xhrOnLoad(_this.xhr);
34565         }
34566         
34567         this.xhr.onerror = function()
34568         {
34569             _this.xhrOnError(_this.xhr);
34570         }
34571         
34572         var formData = new FormData();
34573
34574         formData.append('returnHTML', 'NO');
34575         
34576         if(crop){
34577             formData.append('crop', crop);
34578         }
34579         
34580         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
34581             formData.append(this.paramName, file, file.name);
34582         }
34583         
34584         if(typeof(file.filename) != 'undefined'){
34585             formData.append('filename', file.filename);
34586         }
34587         
34588         if(typeof(file.mimetype) != 'undefined'){
34589             formData.append('mimetype', file.mimetype);
34590         }
34591         
34592         if(this.fireEvent('arrange', this, formData) != false){
34593             this.xhr.send(formData);
34594         };
34595     },
34596     
34597     xhrOnLoad : function(xhr)
34598     {
34599         if(this.loadMask){
34600             this.maskEl.unmask();
34601         }
34602         
34603         if (xhr.readyState !== 4) {
34604             this.fireEvent('exception', this, xhr);
34605             return;
34606         }
34607
34608         var response = Roo.decode(xhr.responseText);
34609         
34610         if(!response.success){
34611             this.fireEvent('exception', this, xhr);
34612             return;
34613         }
34614         
34615         var response = Roo.decode(xhr.responseText);
34616         
34617         this.fireEvent('upload', this, response);
34618         
34619     },
34620     
34621     xhrOnError : function()
34622     {
34623         if(this.loadMask){
34624             this.maskEl.unmask();
34625         }
34626         
34627         Roo.log('xhr on error');
34628         
34629         var response = Roo.decode(xhr.responseText);
34630           
34631         Roo.log(response);
34632         
34633     },
34634     
34635     prepare : function(file)
34636     {   
34637         if(this.loadMask){
34638             this.maskEl.mask(this.loadingText);
34639         }
34640         
34641         this.file = false;
34642         this.exif = {};
34643         
34644         if(typeof(file) === 'string'){
34645             this.loadCanvas(file);
34646             return;
34647         }
34648         
34649         if(!file || !this.urlAPI){
34650             return;
34651         }
34652         
34653         this.file = file;
34654         this.cropType = file.type;
34655         
34656         var _this = this;
34657         
34658         if(this.fireEvent('prepare', this, this.file) != false){
34659             
34660             var reader = new FileReader();
34661             
34662             reader.onload = function (e) {
34663                 if (e.target.error) {
34664                     Roo.log(e.target.error);
34665                     return;
34666                 }
34667                 
34668                 var buffer = e.target.result,
34669                     dataView = new DataView(buffer),
34670                     offset = 2,
34671                     maxOffset = dataView.byteLength - 4,
34672                     markerBytes,
34673                     markerLength;
34674                 
34675                 if (dataView.getUint16(0) === 0xffd8) {
34676                     while (offset < maxOffset) {
34677                         markerBytes = dataView.getUint16(offset);
34678                         
34679                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
34680                             markerLength = dataView.getUint16(offset + 2) + 2;
34681                             if (offset + markerLength > dataView.byteLength) {
34682                                 Roo.log('Invalid meta data: Invalid segment size.');
34683                                 break;
34684                             }
34685                             
34686                             if(markerBytes == 0xffe1){
34687                                 _this.parseExifData(
34688                                     dataView,
34689                                     offset,
34690                                     markerLength
34691                                 );
34692                             }
34693                             
34694                             offset += markerLength;
34695                             
34696                             continue;
34697                         }
34698                         
34699                         break;
34700                     }
34701                     
34702                 }
34703                 
34704                 var url = _this.urlAPI.createObjectURL(_this.file);
34705                 
34706                 _this.loadCanvas(url);
34707                 
34708                 return;
34709             }
34710             
34711             reader.readAsArrayBuffer(this.file);
34712             
34713         }
34714         
34715     },
34716     
34717     parseExifData : function(dataView, offset, length)
34718     {
34719         var tiffOffset = offset + 10,
34720             littleEndian,
34721             dirOffset;
34722     
34723         if (dataView.getUint32(offset + 4) !== 0x45786966) {
34724             // No Exif data, might be XMP data instead
34725             return;
34726         }
34727         
34728         // Check for the ASCII code for "Exif" (0x45786966):
34729         if (dataView.getUint32(offset + 4) !== 0x45786966) {
34730             // No Exif data, might be XMP data instead
34731             return;
34732         }
34733         if (tiffOffset + 8 > dataView.byteLength) {
34734             Roo.log('Invalid Exif data: Invalid segment size.');
34735             return;
34736         }
34737         // Check for the two null bytes:
34738         if (dataView.getUint16(offset + 8) !== 0x0000) {
34739             Roo.log('Invalid Exif data: Missing byte alignment offset.');
34740             return;
34741         }
34742         // Check the byte alignment:
34743         switch (dataView.getUint16(tiffOffset)) {
34744         case 0x4949:
34745             littleEndian = true;
34746             break;
34747         case 0x4D4D:
34748             littleEndian = false;
34749             break;
34750         default:
34751             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
34752             return;
34753         }
34754         // Check for the TIFF tag marker (0x002A):
34755         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
34756             Roo.log('Invalid Exif data: Missing TIFF marker.');
34757             return;
34758         }
34759         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
34760         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
34761         
34762         this.parseExifTags(
34763             dataView,
34764             tiffOffset,
34765             tiffOffset + dirOffset,
34766             littleEndian
34767         );
34768     },
34769     
34770     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
34771     {
34772         var tagsNumber,
34773             dirEndOffset,
34774             i;
34775         if (dirOffset + 6 > dataView.byteLength) {
34776             Roo.log('Invalid Exif data: Invalid directory offset.');
34777             return;
34778         }
34779         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
34780         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
34781         if (dirEndOffset + 4 > dataView.byteLength) {
34782             Roo.log('Invalid Exif data: Invalid directory size.');
34783             return;
34784         }
34785         for (i = 0; i < tagsNumber; i += 1) {
34786             this.parseExifTag(
34787                 dataView,
34788                 tiffOffset,
34789                 dirOffset + 2 + 12 * i, // tag offset
34790                 littleEndian
34791             );
34792         }
34793         // Return the offset to the next directory:
34794         return dataView.getUint32(dirEndOffset, littleEndian);
34795     },
34796     
34797     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
34798     {
34799         var tag = dataView.getUint16(offset, littleEndian);
34800         
34801         this.exif[tag] = this.getExifValue(
34802             dataView,
34803             tiffOffset,
34804             offset,
34805             dataView.getUint16(offset + 2, littleEndian), // tag type
34806             dataView.getUint32(offset + 4, littleEndian), // tag length
34807             littleEndian
34808         );
34809     },
34810     
34811     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
34812     {
34813         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
34814             tagSize,
34815             dataOffset,
34816             values,
34817             i,
34818             str,
34819             c;
34820     
34821         if (!tagType) {
34822             Roo.log('Invalid Exif data: Invalid tag type.');
34823             return;
34824         }
34825         
34826         tagSize = tagType.size * length;
34827         // Determine if the value is contained in the dataOffset bytes,
34828         // or if the value at the dataOffset is a pointer to the actual data:
34829         dataOffset = tagSize > 4 ?
34830                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
34831         if (dataOffset + tagSize > dataView.byteLength) {
34832             Roo.log('Invalid Exif data: Invalid data offset.');
34833             return;
34834         }
34835         if (length === 1) {
34836             return tagType.getValue(dataView, dataOffset, littleEndian);
34837         }
34838         values = [];
34839         for (i = 0; i < length; i += 1) {
34840             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
34841         }
34842         
34843         if (tagType.ascii) {
34844             str = '';
34845             // Concatenate the chars:
34846             for (i = 0; i < values.length; i += 1) {
34847                 c = values[i];
34848                 // Ignore the terminating NULL byte(s):
34849                 if (c === '\u0000') {
34850                     break;
34851                 }
34852                 str += c;
34853             }
34854             return str;
34855         }
34856         return values;
34857     }
34858     
34859 });
34860
34861 Roo.apply(Roo.bootstrap.UploadCropbox, {
34862     tags : {
34863         'Orientation': 0x0112
34864     },
34865     
34866     Orientation: {
34867             1: 0, //'top-left',
34868 //            2: 'top-right',
34869             3: 180, //'bottom-right',
34870 //            4: 'bottom-left',
34871 //            5: 'left-top',
34872             6: 90, //'right-top',
34873 //            7: 'right-bottom',
34874             8: 270 //'left-bottom'
34875     },
34876     
34877     exifTagTypes : {
34878         // byte, 8-bit unsigned int:
34879         1: {
34880             getValue: function (dataView, dataOffset) {
34881                 return dataView.getUint8(dataOffset);
34882             },
34883             size: 1
34884         },
34885         // ascii, 8-bit byte:
34886         2: {
34887             getValue: function (dataView, dataOffset) {
34888                 return String.fromCharCode(dataView.getUint8(dataOffset));
34889             },
34890             size: 1,
34891             ascii: true
34892         },
34893         // short, 16 bit int:
34894         3: {
34895             getValue: function (dataView, dataOffset, littleEndian) {
34896                 return dataView.getUint16(dataOffset, littleEndian);
34897             },
34898             size: 2
34899         },
34900         // long, 32 bit int:
34901         4: {
34902             getValue: function (dataView, dataOffset, littleEndian) {
34903                 return dataView.getUint32(dataOffset, littleEndian);
34904             },
34905             size: 4
34906         },
34907         // rational = two long values, first is numerator, second is denominator:
34908         5: {
34909             getValue: function (dataView, dataOffset, littleEndian) {
34910                 return dataView.getUint32(dataOffset, littleEndian) /
34911                     dataView.getUint32(dataOffset + 4, littleEndian);
34912             },
34913             size: 8
34914         },
34915         // slong, 32 bit signed int:
34916         9: {
34917             getValue: function (dataView, dataOffset, littleEndian) {
34918                 return dataView.getInt32(dataOffset, littleEndian);
34919             },
34920             size: 4
34921         },
34922         // srational, two slongs, first is numerator, second is denominator:
34923         10: {
34924             getValue: function (dataView, dataOffset, littleEndian) {
34925                 return dataView.getInt32(dataOffset, littleEndian) /
34926                     dataView.getInt32(dataOffset + 4, littleEndian);
34927             },
34928             size: 8
34929         }
34930     },
34931     
34932     footer : {
34933         STANDARD : [
34934             {
34935                 tag : 'div',
34936                 cls : 'btn-group roo-upload-cropbox-rotate-left',
34937                 action : 'rotate-left',
34938                 cn : [
34939                     {
34940                         tag : 'button',
34941                         cls : 'btn btn-default',
34942                         html : '<i class="fa fa-undo"></i>'
34943                     }
34944                 ]
34945             },
34946             {
34947                 tag : 'div',
34948                 cls : 'btn-group roo-upload-cropbox-picture',
34949                 action : 'picture',
34950                 cn : [
34951                     {
34952                         tag : 'button',
34953                         cls : 'btn btn-default',
34954                         html : '<i class="fa fa-picture-o"></i>'
34955                     }
34956                 ]
34957             },
34958             {
34959                 tag : 'div',
34960                 cls : 'btn-group roo-upload-cropbox-rotate-right',
34961                 action : 'rotate-right',
34962                 cn : [
34963                     {
34964                         tag : 'button',
34965                         cls : 'btn btn-default',
34966                         html : '<i class="fa fa-repeat"></i>'
34967                     }
34968                 ]
34969             }
34970         ],
34971         DOCUMENT : [
34972             {
34973                 tag : 'div',
34974                 cls : 'btn-group roo-upload-cropbox-rotate-left',
34975                 action : 'rotate-left',
34976                 cn : [
34977                     {
34978                         tag : 'button',
34979                         cls : 'btn btn-default',
34980                         html : '<i class="fa fa-undo"></i>'
34981                     }
34982                 ]
34983             },
34984             {
34985                 tag : 'div',
34986                 cls : 'btn-group roo-upload-cropbox-download',
34987                 action : 'download',
34988                 cn : [
34989                     {
34990                         tag : 'button',
34991                         cls : 'btn btn-default',
34992                         html : '<i class="fa fa-download"></i>'
34993                     }
34994                 ]
34995             },
34996             {
34997                 tag : 'div',
34998                 cls : 'btn-group roo-upload-cropbox-crop',
34999                 action : 'crop',
35000                 cn : [
35001                     {
35002                         tag : 'button',
35003                         cls : 'btn btn-default',
35004                         html : '<i class="fa fa-crop"></i>'
35005                     }
35006                 ]
35007             },
35008             {
35009                 tag : 'div',
35010                 cls : 'btn-group roo-upload-cropbox-trash',
35011                 action : 'trash',
35012                 cn : [
35013                     {
35014                         tag : 'button',
35015                         cls : 'btn btn-default',
35016                         html : '<i class="fa fa-trash"></i>'
35017                     }
35018                 ]
35019             },
35020             {
35021                 tag : 'div',
35022                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35023                 action : 'rotate-right',
35024                 cn : [
35025                     {
35026                         tag : 'button',
35027                         cls : 'btn btn-default',
35028                         html : '<i class="fa fa-repeat"></i>'
35029                     }
35030                 ]
35031             }
35032         ],
35033         ROTATOR : [
35034             {
35035                 tag : 'div',
35036                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35037                 action : 'rotate-left',
35038                 cn : [
35039                     {
35040                         tag : 'button',
35041                         cls : 'btn btn-default',
35042                         html : '<i class="fa fa-undo"></i>'
35043                     }
35044                 ]
35045             },
35046             {
35047                 tag : 'div',
35048                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35049                 action : 'rotate-right',
35050                 cn : [
35051                     {
35052                         tag : 'button',
35053                         cls : 'btn btn-default',
35054                         html : '<i class="fa fa-repeat"></i>'
35055                     }
35056                 ]
35057             }
35058         ]
35059     }
35060 });
35061
35062 /*
35063 * Licence: LGPL
35064 */
35065
35066 /**
35067  * @class Roo.bootstrap.DocumentManager
35068  * @extends Roo.bootstrap.Component
35069  * Bootstrap DocumentManager class
35070  * @cfg {String} paramName default 'imageUpload'
35071  * @cfg {String} toolTipName default 'filename'
35072  * @cfg {String} method default POST
35073  * @cfg {String} url action url
35074  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
35075  * @cfg {Boolean} multiple multiple upload default true
35076  * @cfg {Number} thumbSize default 300
35077  * @cfg {String} fieldLabel
35078  * @cfg {Number} labelWidth default 4
35079  * @cfg {String} labelAlign (left|top) default left
35080  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
35081 * @cfg {Number} labellg set the width of label (1-12)
35082  * @cfg {Number} labelmd set the width of label (1-12)
35083  * @cfg {Number} labelsm set the width of label (1-12)
35084  * @cfg {Number} labelxs set the width of label (1-12)
35085  * 
35086  * @constructor
35087  * Create a new DocumentManager
35088  * @param {Object} config The config object
35089  */
35090
35091 Roo.bootstrap.DocumentManager = function(config){
35092     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
35093     
35094     this.files = [];
35095     this.delegates = [];
35096     
35097     this.addEvents({
35098         /**
35099          * @event initial
35100          * Fire when initial the DocumentManager
35101          * @param {Roo.bootstrap.DocumentManager} this
35102          */
35103         "initial" : true,
35104         /**
35105          * @event inspect
35106          * inspect selected file
35107          * @param {Roo.bootstrap.DocumentManager} this
35108          * @param {File} file
35109          */
35110         "inspect" : true,
35111         /**
35112          * @event exception
35113          * Fire when xhr load exception
35114          * @param {Roo.bootstrap.DocumentManager} this
35115          * @param {XMLHttpRequest} xhr
35116          */
35117         "exception" : true,
35118         /**
35119          * @event afterupload
35120          * Fire when xhr load exception
35121          * @param {Roo.bootstrap.DocumentManager} this
35122          * @param {XMLHttpRequest} xhr
35123          */
35124         "afterupload" : true,
35125         /**
35126          * @event prepare
35127          * prepare the form data
35128          * @param {Roo.bootstrap.DocumentManager} this
35129          * @param {Object} formData
35130          */
35131         "prepare" : true,
35132         /**
35133          * @event remove
35134          * Fire when remove the file
35135          * @param {Roo.bootstrap.DocumentManager} this
35136          * @param {Object} file
35137          */
35138         "remove" : true,
35139         /**
35140          * @event refresh
35141          * Fire after refresh the file
35142          * @param {Roo.bootstrap.DocumentManager} this
35143          */
35144         "refresh" : true,
35145         /**
35146          * @event click
35147          * Fire after click the image
35148          * @param {Roo.bootstrap.DocumentManager} this
35149          * @param {Object} file
35150          */
35151         "click" : true,
35152         /**
35153          * @event edit
35154          * Fire when upload a image and editable set to true
35155          * @param {Roo.bootstrap.DocumentManager} this
35156          * @param {Object} file
35157          */
35158         "edit" : true,
35159         /**
35160          * @event beforeselectfile
35161          * Fire before select file
35162          * @param {Roo.bootstrap.DocumentManager} this
35163          */
35164         "beforeselectfile" : true,
35165         /**
35166          * @event process
35167          * Fire before process file
35168          * @param {Roo.bootstrap.DocumentManager} this
35169          * @param {Object} file
35170          */
35171         "process" : true,
35172         /**
35173          * @event previewrendered
35174          * Fire when preview rendered
35175          * @param {Roo.bootstrap.DocumentManager} this
35176          * @param {Object} file
35177          */
35178         "previewrendered" : true,
35179         /**
35180          */
35181         "previewResize" : true
35182         
35183     });
35184 };
35185
35186 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
35187     
35188     boxes : 0,
35189     inputName : '',
35190     thumbSize : 300,
35191     multiple : true,
35192     files : false,
35193     method : 'POST',
35194     url : '',
35195     paramName : 'imageUpload',
35196     toolTipName : 'filename',
35197     fieldLabel : '',
35198     labelWidth : 4,
35199     labelAlign : 'left',
35200     editable : true,
35201     delegates : false,
35202     xhr : false, 
35203     
35204     labellg : 0,
35205     labelmd : 0,
35206     labelsm : 0,
35207     labelxs : 0,
35208     
35209     getAutoCreate : function()
35210     {   
35211         var managerWidget = {
35212             tag : 'div',
35213             cls : 'roo-document-manager',
35214             cn : [
35215                 {
35216                     tag : 'input',
35217                     cls : 'roo-document-manager-selector',
35218                     type : 'file'
35219                 },
35220                 {
35221                     tag : 'div',
35222                     cls : 'roo-document-manager-uploader',
35223                     cn : [
35224                         {
35225                             tag : 'div',
35226                             cls : 'roo-document-manager-upload-btn',
35227                             html : '<i class="fa fa-plus"></i>'
35228                         }
35229                     ]
35230                     
35231                 }
35232             ]
35233         };
35234         
35235         var content = [
35236             {
35237                 tag : 'div',
35238                 cls : 'column col-md-12',
35239                 cn : managerWidget
35240             }
35241         ];
35242         
35243         if(this.fieldLabel.length){
35244             
35245             content = [
35246                 {
35247                     tag : 'div',
35248                     cls : 'column col-md-12',
35249                     html : this.fieldLabel
35250                 },
35251                 {
35252                     tag : 'div',
35253                     cls : 'column col-md-12',
35254                     cn : managerWidget
35255                 }
35256             ];
35257
35258             if(this.labelAlign == 'left'){
35259                 content = [
35260                     {
35261                         tag : 'div',
35262                         cls : 'column',
35263                         html : this.fieldLabel
35264                     },
35265                     {
35266                         tag : 'div',
35267                         cls : 'column',
35268                         cn : managerWidget
35269                     }
35270                 ];
35271                 
35272                 if(this.labelWidth > 12){
35273                     content[0].style = "width: " + this.labelWidth + 'px';
35274                 }
35275
35276                 if(this.labelWidth < 13 && this.labelmd == 0){
35277                     this.labelmd = this.labelWidth;
35278                 }
35279
35280                 if(this.labellg > 0){
35281                     content[0].cls += ' col-lg-' + this.labellg;
35282                     content[1].cls += ' col-lg-' + (12 - this.labellg);
35283                 }
35284
35285                 if(this.labelmd > 0){
35286                     content[0].cls += ' col-md-' + this.labelmd;
35287                     content[1].cls += ' col-md-' + (12 - this.labelmd);
35288                 }
35289
35290                 if(this.labelsm > 0){
35291                     content[0].cls += ' col-sm-' + this.labelsm;
35292                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
35293                 }
35294
35295                 if(this.labelxs > 0){
35296                     content[0].cls += ' col-xs-' + this.labelxs;
35297                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
35298                 }
35299                 
35300             }
35301         }
35302         
35303         var cfg = {
35304             tag : 'div',
35305             cls : 'row clearfix',
35306             cn : content
35307         };
35308         
35309         return cfg;
35310         
35311     },
35312     
35313     initEvents : function()
35314     {
35315         this.managerEl = this.el.select('.roo-document-manager', true).first();
35316         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35317         
35318         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
35319         this.selectorEl.hide();
35320         
35321         if(this.multiple){
35322             this.selectorEl.attr('multiple', 'multiple');
35323         }
35324         
35325         this.selectorEl.on('change', this.onFileSelected, this);
35326         
35327         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
35328         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35329         
35330         this.uploader.on('click', this.onUploaderClick, this);
35331         
35332         this.renderProgressDialog();
35333         
35334         var _this = this;
35335         
35336         window.addEventListener("resize", function() { _this.refresh(); } );
35337         
35338         this.fireEvent('initial', this);
35339     },
35340     
35341     renderProgressDialog : function()
35342     {
35343         var _this = this;
35344         
35345         this.progressDialog = new Roo.bootstrap.Modal({
35346             cls : 'roo-document-manager-progress-dialog',
35347             allow_close : false,
35348             animate : false,
35349             title : '',
35350             buttons : [
35351                 {
35352                     name  :'cancel',
35353                     weight : 'danger',
35354                     html : 'Cancel'
35355                 }
35356             ], 
35357             listeners : { 
35358                 btnclick : function() {
35359                     _this.uploadCancel();
35360                     this.hide();
35361                 }
35362             }
35363         });
35364          
35365         this.progressDialog.render(Roo.get(document.body));
35366          
35367         this.progress = new Roo.bootstrap.Progress({
35368             cls : 'roo-document-manager-progress',
35369             active : true,
35370             striped : true
35371         });
35372         
35373         this.progress.render(this.progressDialog.getChildContainer());
35374         
35375         this.progressBar = new Roo.bootstrap.ProgressBar({
35376             cls : 'roo-document-manager-progress-bar',
35377             aria_valuenow : 0,
35378             aria_valuemin : 0,
35379             aria_valuemax : 12,
35380             panel : 'success'
35381         });
35382         
35383         this.progressBar.render(this.progress.getChildContainer());
35384     },
35385     
35386     onUploaderClick : function(e)
35387     {
35388         e.preventDefault();
35389      
35390         if(this.fireEvent('beforeselectfile', this) != false){
35391             this.selectorEl.dom.click();
35392         }
35393         
35394     },
35395     
35396     onFileSelected : function(e)
35397     {
35398         e.preventDefault();
35399         
35400         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35401             return;
35402         }
35403         
35404         Roo.each(this.selectorEl.dom.files, function(file){
35405             if(this.fireEvent('inspect', this, file) != false){
35406                 this.files.push(file);
35407             }
35408         }, this);
35409         
35410         this.queue();
35411         
35412     },
35413     
35414     queue : function()
35415     {
35416         this.selectorEl.dom.value = '';
35417         
35418         if(!this.files || !this.files.length){
35419             return;
35420         }
35421         
35422         if(this.boxes > 0 && this.files.length > this.boxes){
35423             this.files = this.files.slice(0, this.boxes);
35424         }
35425         
35426         this.uploader.show();
35427         
35428         if(this.boxes > 0 && this.files.length > this.boxes - 1){
35429             this.uploader.hide();
35430         }
35431         
35432         var _this = this;
35433         
35434         var files = [];
35435         
35436         var docs = [];
35437         
35438         Roo.each(this.files, function(file){
35439             
35440             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35441                 var f = this.renderPreview(file);
35442                 files.push(f);
35443                 return;
35444             }
35445             
35446             if(file.type.indexOf('image') != -1){
35447                 this.delegates.push(
35448                     (function(){
35449                         _this.process(file);
35450                     }).createDelegate(this)
35451                 );
35452         
35453                 return;
35454             }
35455             
35456             docs.push(
35457                 (function(){
35458                     _this.process(file);
35459                 }).createDelegate(this)
35460             );
35461             
35462         }, this);
35463         
35464         this.files = files;
35465         
35466         this.delegates = this.delegates.concat(docs);
35467         
35468         if(!this.delegates.length){
35469             this.refresh();
35470             return;
35471         }
35472         
35473         this.progressBar.aria_valuemax = this.delegates.length;
35474         
35475         this.arrange();
35476         
35477         return;
35478     },
35479     
35480     arrange : function()
35481     {
35482         if(!this.delegates.length){
35483             this.progressDialog.hide();
35484             this.refresh();
35485             return;
35486         }
35487         
35488         var delegate = this.delegates.shift();
35489         
35490         this.progressDialog.show();
35491         
35492         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
35493         
35494         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
35495         
35496         delegate();
35497     },
35498     
35499     refresh : function()
35500     {
35501         this.uploader.show();
35502         
35503         if(this.boxes > 0 && this.files.length > this.boxes - 1){
35504             this.uploader.hide();
35505         }
35506         
35507         Roo.isTouch ? this.closable(false) : this.closable(true);
35508         
35509         this.fireEvent('refresh', this);
35510     },
35511     
35512     onRemove : function(e, el, o)
35513     {
35514         e.preventDefault();
35515         
35516         this.fireEvent('remove', this, o);
35517         
35518     },
35519     
35520     remove : function(o)
35521     {
35522         var files = [];
35523         
35524         Roo.each(this.files, function(file){
35525             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
35526                 files.push(file);
35527                 return;
35528             }
35529
35530             o.target.remove();
35531
35532         }, this);
35533         
35534         this.files = files;
35535         
35536         this.refresh();
35537     },
35538     
35539     clear : function()
35540     {
35541         Roo.each(this.files, function(file){
35542             if(!file.target){
35543                 return;
35544             }
35545             
35546             file.target.remove();
35547
35548         }, this);
35549         
35550         this.files = [];
35551         
35552         this.refresh();
35553     },
35554     
35555     onClick : function(e, el, o)
35556     {
35557         e.preventDefault();
35558         
35559         this.fireEvent('click', this, o);
35560         
35561     },
35562     
35563     closable : function(closable)
35564     {
35565         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
35566             
35567             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35568             
35569             if(closable){
35570                 el.show();
35571                 return;
35572             }
35573             
35574             el.hide();
35575             
35576         }, this);
35577     },
35578     
35579     xhrOnLoad : function(xhr)
35580     {
35581         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35582             el.remove();
35583         }, this);
35584         
35585         if (xhr.readyState !== 4) {
35586             this.arrange();
35587             this.fireEvent('exception', this, xhr);
35588             return;
35589         }
35590
35591         var response = Roo.decode(xhr.responseText);
35592         
35593         if(!response.success){
35594             this.arrange();
35595             this.fireEvent('exception', this, xhr);
35596             return;
35597         }
35598         
35599         var file = this.renderPreview(response.data);
35600         
35601         this.files.push(file);
35602         
35603         this.arrange();
35604         
35605         this.fireEvent('afterupload', this, xhr);
35606         
35607     },
35608     
35609     xhrOnError : function(xhr)
35610     {
35611         Roo.log('xhr on error');
35612         
35613         var response = Roo.decode(xhr.responseText);
35614           
35615         Roo.log(response);
35616         
35617         this.arrange();
35618     },
35619     
35620     process : function(file)
35621     {
35622         if(this.fireEvent('process', this, file) !== false){
35623             if(this.editable && file.type.indexOf('image') != -1){
35624                 this.fireEvent('edit', this, file);
35625                 return;
35626             }
35627
35628             this.uploadStart(file, false);
35629
35630             return;
35631         }
35632         
35633     },
35634     
35635     uploadStart : function(file, crop)
35636     {
35637         this.xhr = new XMLHttpRequest();
35638         
35639         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35640             this.arrange();
35641             return;
35642         }
35643         
35644         file.xhr = this.xhr;
35645             
35646         this.managerEl.createChild({
35647             tag : 'div',
35648             cls : 'roo-document-manager-loading',
35649             cn : [
35650                 {
35651                     tag : 'div',
35652                     tooltip : file.name,
35653                     cls : 'roo-document-manager-thumb',
35654                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
35655                 }
35656             ]
35657
35658         });
35659
35660         this.xhr.open(this.method, this.url, true);
35661         
35662         var headers = {
35663             "Accept": "application/json",
35664             "Cache-Control": "no-cache",
35665             "X-Requested-With": "XMLHttpRequest"
35666         };
35667         
35668         for (var headerName in headers) {
35669             var headerValue = headers[headerName];
35670             if (headerValue) {
35671                 this.xhr.setRequestHeader(headerName, headerValue);
35672             }
35673         }
35674         
35675         var _this = this;
35676         
35677         this.xhr.onload = function()
35678         {
35679             _this.xhrOnLoad(_this.xhr);
35680         }
35681         
35682         this.xhr.onerror = function()
35683         {
35684             _this.xhrOnError(_this.xhr);
35685         }
35686         
35687         var formData = new FormData();
35688
35689         formData.append('returnHTML', 'NO');
35690         
35691         if(crop){
35692             formData.append('crop', crop);
35693         }
35694         
35695         formData.append(this.paramName, file, file.name);
35696         
35697         var options = {
35698             file : file, 
35699             manually : false
35700         };
35701         
35702         if(this.fireEvent('prepare', this, formData, options) != false){
35703             
35704             if(options.manually){
35705                 return;
35706             }
35707             
35708             this.xhr.send(formData);
35709             return;
35710         };
35711         
35712         this.uploadCancel();
35713     },
35714     
35715     uploadCancel : function()
35716     {
35717         if (this.xhr) {
35718             this.xhr.abort();
35719         }
35720         
35721         this.delegates = [];
35722         
35723         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35724             el.remove();
35725         }, this);
35726         
35727         this.arrange();
35728     },
35729     
35730     renderPreview : function(file)
35731     {
35732         if(typeof(file.target) != 'undefined' && file.target){
35733             return file;
35734         }
35735         
35736         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
35737         
35738         var previewEl = this.managerEl.createChild({
35739             tag : 'div',
35740             cls : 'roo-document-manager-preview',
35741             cn : [
35742                 {
35743                     tag : 'div',
35744                     tooltip : file[this.toolTipName],
35745                     cls : 'roo-document-manager-thumb',
35746                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
35747                 },
35748                 {
35749                     tag : 'button',
35750                     cls : 'close',
35751                     html : '<i class="fa fa-times-circle"></i>'
35752                 }
35753             ]
35754         });
35755
35756         var close = previewEl.select('button.close', true).first();
35757
35758         close.on('click', this.onRemove, this, file);
35759
35760         file.target = previewEl;
35761
35762         var image = previewEl.select('img', true).first();
35763         
35764         var _this = this;
35765         
35766         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
35767         
35768         image.on('click', this.onClick, this, file);
35769         
35770         this.fireEvent('previewrendered', this, file);
35771         
35772         return file;
35773         
35774     },
35775     
35776     onPreviewLoad : function(file, image)
35777     {
35778         if(typeof(file.target) == 'undefined' || !file.target){
35779             return;
35780         }
35781         
35782         var width = image.dom.naturalWidth || image.dom.width;
35783         var height = image.dom.naturalHeight || image.dom.height;
35784         
35785         if(!this.previewResize) {
35786             return;
35787         }
35788         
35789         if(width > height){
35790             file.target.addClass('wide');
35791             return;
35792         }
35793         
35794         file.target.addClass('tall');
35795         return;
35796         
35797     },
35798     
35799     uploadFromSource : function(file, crop)
35800     {
35801         this.xhr = new XMLHttpRequest();
35802         
35803         this.managerEl.createChild({
35804             tag : 'div',
35805             cls : 'roo-document-manager-loading',
35806             cn : [
35807                 {
35808                     tag : 'div',
35809                     tooltip : file.name,
35810                     cls : 'roo-document-manager-thumb',
35811                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
35812                 }
35813             ]
35814
35815         });
35816
35817         this.xhr.open(this.method, this.url, true);
35818         
35819         var headers = {
35820             "Accept": "application/json",
35821             "Cache-Control": "no-cache",
35822             "X-Requested-With": "XMLHttpRequest"
35823         };
35824         
35825         for (var headerName in headers) {
35826             var headerValue = headers[headerName];
35827             if (headerValue) {
35828                 this.xhr.setRequestHeader(headerName, headerValue);
35829             }
35830         }
35831         
35832         var _this = this;
35833         
35834         this.xhr.onload = function()
35835         {
35836             _this.xhrOnLoad(_this.xhr);
35837         }
35838         
35839         this.xhr.onerror = function()
35840         {
35841             _this.xhrOnError(_this.xhr);
35842         }
35843         
35844         var formData = new FormData();
35845
35846         formData.append('returnHTML', 'NO');
35847         
35848         formData.append('crop', crop);
35849         
35850         if(typeof(file.filename) != 'undefined'){
35851             formData.append('filename', file.filename);
35852         }
35853         
35854         if(typeof(file.mimetype) != 'undefined'){
35855             formData.append('mimetype', file.mimetype);
35856         }
35857         
35858         Roo.log(formData);
35859         
35860         if(this.fireEvent('prepare', this, formData) != false){
35861             this.xhr.send(formData);
35862         };
35863     }
35864 });
35865
35866 /*
35867 * Licence: LGPL
35868 */
35869
35870 /**
35871  * @class Roo.bootstrap.DocumentViewer
35872  * @extends Roo.bootstrap.Component
35873  * Bootstrap DocumentViewer class
35874  * @cfg {Boolean} showDownload (true|false) show download button (default true)
35875  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
35876  * 
35877  * @constructor
35878  * Create a new DocumentViewer
35879  * @param {Object} config The config object
35880  */
35881
35882 Roo.bootstrap.DocumentViewer = function(config){
35883     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
35884     
35885     this.addEvents({
35886         /**
35887          * @event initial
35888          * Fire after initEvent
35889          * @param {Roo.bootstrap.DocumentViewer} this
35890          */
35891         "initial" : true,
35892         /**
35893          * @event click
35894          * Fire after click
35895          * @param {Roo.bootstrap.DocumentViewer} this
35896          */
35897         "click" : true,
35898         /**
35899          * @event download
35900          * Fire after download button
35901          * @param {Roo.bootstrap.DocumentViewer} this
35902          */
35903         "download" : true,
35904         /**
35905          * @event trash
35906          * Fire after trash button
35907          * @param {Roo.bootstrap.DocumentViewer} this
35908          */
35909         "trash" : true
35910         
35911     });
35912 };
35913
35914 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
35915     
35916     showDownload : true,
35917     
35918     showTrash : true,
35919     
35920     getAutoCreate : function()
35921     {
35922         var cfg = {
35923             tag : 'div',
35924             cls : 'roo-document-viewer',
35925             cn : [
35926                 {
35927                     tag : 'div',
35928                     cls : 'roo-document-viewer-body',
35929                     cn : [
35930                         {
35931                             tag : 'div',
35932                             cls : 'roo-document-viewer-thumb',
35933                             cn : [
35934                                 {
35935                                     tag : 'img',
35936                                     cls : 'roo-document-viewer-image'
35937                                 }
35938                             ]
35939                         }
35940                     ]
35941                 },
35942                 {
35943                     tag : 'div',
35944                     cls : 'roo-document-viewer-footer',
35945                     cn : {
35946                         tag : 'div',
35947                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
35948                         cn : [
35949                             {
35950                                 tag : 'div',
35951                                 cls : 'btn-group roo-document-viewer-download',
35952                                 cn : [
35953                                     {
35954                                         tag : 'button',
35955                                         cls : 'btn btn-default',
35956                                         html : '<i class="fa fa-download"></i>'
35957                                     }
35958                                 ]
35959                             },
35960                             {
35961                                 tag : 'div',
35962                                 cls : 'btn-group roo-document-viewer-trash',
35963                                 cn : [
35964                                     {
35965                                         tag : 'button',
35966                                         cls : 'btn btn-default',
35967                                         html : '<i class="fa fa-trash"></i>'
35968                                     }
35969                                 ]
35970                             }
35971                         ]
35972                     }
35973                 }
35974             ]
35975         };
35976         
35977         return cfg;
35978     },
35979     
35980     initEvents : function()
35981     {
35982         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
35983         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35984         
35985         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
35986         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35987         
35988         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
35989         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35990         
35991         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
35992         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
35993         
35994         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
35995         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
35996         
35997         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
35998         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
35999         
36000         this.bodyEl.on('click', this.onClick, this);
36001         this.downloadBtn.on('click', this.onDownload, this);
36002         this.trashBtn.on('click', this.onTrash, this);
36003         
36004         this.downloadBtn.hide();
36005         this.trashBtn.hide();
36006         
36007         if(this.showDownload){
36008             this.downloadBtn.show();
36009         }
36010         
36011         if(this.showTrash){
36012             this.trashBtn.show();
36013         }
36014         
36015         if(!this.showDownload && !this.showTrash) {
36016             this.footerEl.hide();
36017         }
36018         
36019     },
36020     
36021     initial : function()
36022     {
36023         this.fireEvent('initial', this);
36024         
36025     },
36026     
36027     onClick : function(e)
36028     {
36029         e.preventDefault();
36030         
36031         this.fireEvent('click', this);
36032     },
36033     
36034     onDownload : function(e)
36035     {
36036         e.preventDefault();
36037         
36038         this.fireEvent('download', this);
36039     },
36040     
36041     onTrash : function(e)
36042     {
36043         e.preventDefault();
36044         
36045         this.fireEvent('trash', this);
36046     }
36047     
36048 });
36049 /*
36050  * - LGPL
36051  *
36052  * FieldLabel
36053  * 
36054  */
36055
36056 /**
36057  * @class Roo.bootstrap.form.FieldLabel
36058  * @extends Roo.bootstrap.Component
36059  * Bootstrap FieldLabel class
36060  * @cfg {String} html contents of the element
36061  * @cfg {String} tag tag of the element default label
36062  * @cfg {String} cls class of the element
36063  * @cfg {String} target label target 
36064  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
36065  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
36066  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
36067  * @cfg {String} iconTooltip default "This field is required"
36068  * @cfg {String} indicatorpos (left|right) default left
36069  * 
36070  * @constructor
36071  * Create a new FieldLabel
36072  * @param {Object} config The config object
36073  */
36074
36075 Roo.bootstrap.form.FieldLabel = function(config){
36076     Roo.bootstrap.Element.superclass.constructor.call(this, config);
36077     
36078     this.addEvents({
36079             /**
36080              * @event invalid
36081              * Fires after the field has been marked as invalid.
36082              * @param {Roo.form.FieldLabel} this
36083              * @param {String} msg The validation message
36084              */
36085             invalid : true,
36086             /**
36087              * @event valid
36088              * Fires after the field has been validated with no errors.
36089              * @param {Roo.form.FieldLabel} this
36090              */
36091             valid : true
36092         });
36093 };
36094
36095 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
36096     
36097     tag: 'label',
36098     cls: '',
36099     html: '',
36100     target: '',
36101     allowBlank : true,
36102     invalidClass : 'has-warning',
36103     validClass : 'has-success',
36104     iconTooltip : 'This field is required',
36105     indicatorpos : 'left',
36106     
36107     getAutoCreate : function(){
36108         
36109         var cls = "";
36110         if (!this.allowBlank) {
36111             cls  = "visible";
36112         }
36113         
36114         var cfg = {
36115             tag : this.tag,
36116             cls : 'roo-bootstrap-field-label ' + this.cls,
36117             for : this.target,
36118             cn : [
36119                 {
36120                     tag : 'i',
36121                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
36122                     tooltip : this.iconTooltip
36123                 },
36124                 {
36125                     tag : 'span',
36126                     html : this.html
36127                 }
36128             ] 
36129         };
36130         
36131         if(this.indicatorpos == 'right'){
36132             var cfg = {
36133                 tag : this.tag,
36134                 cls : 'roo-bootstrap-field-label ' + this.cls,
36135                 for : this.target,
36136                 cn : [
36137                     {
36138                         tag : 'span',
36139                         html : this.html
36140                     },
36141                     {
36142                         tag : 'i',
36143                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
36144                         tooltip : this.iconTooltip
36145                     }
36146                 ] 
36147             };
36148         }
36149         
36150         return cfg;
36151     },
36152     
36153     initEvents: function() 
36154     {
36155         Roo.bootstrap.Element.superclass.initEvents.call(this);
36156         
36157         this.indicator = this.indicatorEl();
36158         
36159         if(this.indicator){
36160             this.indicator.removeClass('visible');
36161             this.indicator.addClass('invisible');
36162         }
36163         
36164         Roo.bootstrap.form.FieldLabel.register(this);
36165     },
36166     
36167     indicatorEl : function()
36168     {
36169         var indicator = this.el.select('i.roo-required-indicator',true).first();
36170         
36171         if(!indicator){
36172             return false;
36173         }
36174         
36175         return indicator;
36176         
36177     },
36178     
36179     /**
36180      * Mark this field as valid
36181      */
36182     markValid : function()
36183     {
36184         if(this.indicator){
36185             this.indicator.removeClass('visible');
36186             this.indicator.addClass('invisible');
36187         }
36188         if (Roo.bootstrap.version == 3) {
36189             this.el.removeClass(this.invalidClass);
36190             this.el.addClass(this.validClass);
36191         } else {
36192             this.el.removeClass('is-invalid');
36193             this.el.addClass('is-valid');
36194         }
36195         
36196         
36197         this.fireEvent('valid', this);
36198     },
36199     
36200     /**
36201      * Mark this field as invalid
36202      * @param {String} msg The validation message
36203      */
36204     markInvalid : function(msg)
36205     {
36206         if(this.indicator){
36207             this.indicator.removeClass('invisible');
36208             this.indicator.addClass('visible');
36209         }
36210           if (Roo.bootstrap.version == 3) {
36211             this.el.removeClass(this.validClass);
36212             this.el.addClass(this.invalidClass);
36213         } else {
36214             this.el.removeClass('is-valid');
36215             this.el.addClass('is-invalid');
36216         }
36217         
36218         
36219         this.fireEvent('invalid', this, msg);
36220     }
36221     
36222    
36223 });
36224
36225 Roo.apply(Roo.bootstrap.form.FieldLabel, {
36226     
36227     groups: {},
36228     
36229      /**
36230     * register a FieldLabel Group
36231     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
36232     */
36233     register : function(label)
36234     {
36235         if(this.groups.hasOwnProperty(label.target)){
36236             return;
36237         }
36238      
36239         this.groups[label.target] = label;
36240         
36241     },
36242     /**
36243     * fetch a FieldLabel Group based on the target
36244     * @param {string} target
36245     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
36246     */
36247     get: function(target) {
36248         if (typeof(this.groups[target]) == 'undefined') {
36249             return false;
36250         }
36251         
36252         return this.groups[target] ;
36253     }
36254 });
36255
36256  
36257
36258  /*
36259  * - LGPL
36260  *
36261  * page DateSplitField.
36262  * 
36263  */
36264
36265
36266 /**
36267  * @class Roo.bootstrap.form.DateSplitField
36268  * @extends Roo.bootstrap.Component
36269  * Bootstrap DateSplitField class
36270  * @cfg {string} fieldLabel - the label associated
36271  * @cfg {Number} labelWidth set the width of label (0-12)
36272  * @cfg {String} labelAlign (top|left)
36273  * @cfg {Boolean} dayAllowBlank (true|false) default false
36274  * @cfg {Boolean} monthAllowBlank (true|false) default false
36275  * @cfg {Boolean} yearAllowBlank (true|false) default false
36276  * @cfg {string} dayPlaceholder 
36277  * @cfg {string} monthPlaceholder
36278  * @cfg {string} yearPlaceholder
36279  * @cfg {string} dayFormat default 'd'
36280  * @cfg {string} monthFormat default 'm'
36281  * @cfg {string} yearFormat default 'Y'
36282  * @cfg {Number} labellg set the width of label (1-12)
36283  * @cfg {Number} labelmd set the width of label (1-12)
36284  * @cfg {Number} labelsm set the width of label (1-12)
36285  * @cfg {Number} labelxs set the width of label (1-12)
36286
36287  *     
36288  * @constructor
36289  * Create a new DateSplitField
36290  * @param {Object} config The config object
36291  */
36292
36293 Roo.bootstrap.form.DateSplitField = function(config){
36294     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
36295     
36296     this.addEvents({
36297         // raw events
36298          /**
36299          * @event years
36300          * getting the data of years
36301          * @param {Roo.bootstrap.form.DateSplitField} this
36302          * @param {Object} years
36303          */
36304         "years" : true,
36305         /**
36306          * @event days
36307          * getting the data of days
36308          * @param {Roo.bootstrap.form.DateSplitField} this
36309          * @param {Object} days
36310          */
36311         "days" : true,
36312         /**
36313          * @event invalid
36314          * Fires after the field has been marked as invalid.
36315          * @param {Roo.form.Field} this
36316          * @param {String} msg The validation message
36317          */
36318         invalid : true,
36319        /**
36320          * @event valid
36321          * Fires after the field has been validated with no errors.
36322          * @param {Roo.form.Field} this
36323          */
36324         valid : true
36325     });
36326 };
36327
36328 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
36329     
36330     fieldLabel : '',
36331     labelAlign : 'top',
36332     labelWidth : 3,
36333     dayAllowBlank : false,
36334     monthAllowBlank : false,
36335     yearAllowBlank : false,
36336     dayPlaceholder : '',
36337     monthPlaceholder : '',
36338     yearPlaceholder : '',
36339     dayFormat : 'd',
36340     monthFormat : 'm',
36341     yearFormat : 'Y',
36342     isFormField : true,
36343     labellg : 0,
36344     labelmd : 0,
36345     labelsm : 0,
36346     labelxs : 0,
36347     
36348     getAutoCreate : function()
36349     {
36350         var cfg = {
36351             tag : 'div',
36352             cls : 'row roo-date-split-field-group',
36353             cn : [
36354                 {
36355                     tag : 'input',
36356                     type : 'hidden',
36357                     cls : 'form-hidden-field roo-date-split-field-group-value',
36358                     name : this.name
36359                 }
36360             ]
36361         };
36362         
36363         var labelCls = 'col-md-12';
36364         var contentCls = 'col-md-4';
36365         
36366         if(this.fieldLabel){
36367             
36368             var label = {
36369                 tag : 'div',
36370                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
36371                 cn : [
36372                     {
36373                         tag : 'label',
36374                         html : this.fieldLabel
36375                     }
36376                 ]
36377             };
36378             
36379             if(this.labelAlign == 'left'){
36380             
36381                 if(this.labelWidth > 12){
36382                     label.style = "width: " + this.labelWidth + 'px';
36383                 }
36384
36385                 if(this.labelWidth < 13 && this.labelmd == 0){
36386                     this.labelmd = this.labelWidth;
36387                 }
36388
36389                 if(this.labellg > 0){
36390                     labelCls = ' col-lg-' + this.labellg;
36391                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
36392                 }
36393
36394                 if(this.labelmd > 0){
36395                     labelCls = ' col-md-' + this.labelmd;
36396                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
36397                 }
36398
36399                 if(this.labelsm > 0){
36400                     labelCls = ' col-sm-' + this.labelsm;
36401                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
36402                 }
36403
36404                 if(this.labelxs > 0){
36405                     labelCls = ' col-xs-' + this.labelxs;
36406                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
36407                 }
36408             }
36409             
36410             label.cls += ' ' + labelCls;
36411             
36412             cfg.cn.push(label);
36413         }
36414         
36415         Roo.each(['day', 'month', 'year'], function(t){
36416             cfg.cn.push({
36417                 tag : 'div',
36418                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
36419             });
36420         }, this);
36421         
36422         return cfg;
36423     },
36424     
36425     inputEl: function ()
36426     {
36427         return this.el.select('.roo-date-split-field-group-value', true).first();
36428     },
36429     
36430     onRender : function(ct, position) 
36431     {
36432         var _this = this;
36433         
36434         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
36435         
36436         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
36437         
36438         this.dayField = new Roo.bootstrap.form.ComboBox({
36439             allowBlank : this.dayAllowBlank,
36440             alwaysQuery : true,
36441             displayField : 'value',
36442             editable : false,
36443             fieldLabel : '',
36444             forceSelection : true,
36445             mode : 'local',
36446             placeholder : this.dayPlaceholder,
36447             selectOnFocus : true,
36448             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36449             triggerAction : 'all',
36450             typeAhead : true,
36451             valueField : 'value',
36452             store : new Roo.data.SimpleStore({
36453                 data : (function() {    
36454                     var days = [];
36455                     _this.fireEvent('days', _this, days);
36456                     return days;
36457                 })(),
36458                 fields : [ 'value' ]
36459             }),
36460             listeners : {
36461                 select : function (_self, record, index)
36462                 {
36463                     _this.setValue(_this.getValue());
36464                 }
36465             }
36466         });
36467
36468         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
36469         
36470         this.monthField = new Roo.bootstrap.form.MonthField({
36471             after : '<i class=\"fa fa-calendar\"></i>',
36472             allowBlank : this.monthAllowBlank,
36473             placeholder : this.monthPlaceholder,
36474             readOnly : true,
36475             listeners : {
36476                 render : function (_self)
36477                 {
36478                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
36479                         e.preventDefault();
36480                         _self.focus();
36481                     });
36482                 },
36483                 select : function (_self, oldvalue, newvalue)
36484                 {
36485                     _this.setValue(_this.getValue());
36486                 }
36487             }
36488         });
36489         
36490         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
36491         
36492         this.yearField = new Roo.bootstrap.form.ComboBox({
36493             allowBlank : this.yearAllowBlank,
36494             alwaysQuery : true,
36495             displayField : 'value',
36496             editable : false,
36497             fieldLabel : '',
36498             forceSelection : true,
36499             mode : 'local',
36500             placeholder : this.yearPlaceholder,
36501             selectOnFocus : true,
36502             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36503             triggerAction : 'all',
36504             typeAhead : true,
36505             valueField : 'value',
36506             store : new Roo.data.SimpleStore({
36507                 data : (function() {
36508                     var years = [];
36509                     _this.fireEvent('years', _this, years);
36510                     return years;
36511                 })(),
36512                 fields : [ 'value' ]
36513             }),
36514             listeners : {
36515                 select : function (_self, record, index)
36516                 {
36517                     _this.setValue(_this.getValue());
36518                 }
36519             }
36520         });
36521
36522         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
36523     },
36524     
36525     setValue : function(v, format)
36526     {
36527         this.inputEl.dom.value = v;
36528         
36529         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
36530         
36531         var d = Date.parseDate(v, f);
36532         
36533         if(!d){
36534             this.validate();
36535             return;
36536         }
36537         
36538         this.setDay(d.format(this.dayFormat));
36539         this.setMonth(d.format(this.monthFormat));
36540         this.setYear(d.format(this.yearFormat));
36541         
36542         this.validate();
36543         
36544         return;
36545     },
36546     
36547     setDay : function(v)
36548     {
36549         this.dayField.setValue(v);
36550         this.inputEl.dom.value = this.getValue();
36551         this.validate();
36552         return;
36553     },
36554     
36555     setMonth : function(v)
36556     {
36557         this.monthField.setValue(v, true);
36558         this.inputEl.dom.value = this.getValue();
36559         this.validate();
36560         return;
36561     },
36562     
36563     setYear : function(v)
36564     {
36565         this.yearField.setValue(v);
36566         this.inputEl.dom.value = this.getValue();
36567         this.validate();
36568         return;
36569     },
36570     
36571     getDay : function()
36572     {
36573         return this.dayField.getValue();
36574     },
36575     
36576     getMonth : function()
36577     {
36578         return this.monthField.getValue();
36579     },
36580     
36581     getYear : function()
36582     {
36583         return this.yearField.getValue();
36584     },
36585     
36586     getValue : function()
36587     {
36588         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
36589         
36590         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
36591         
36592         return date;
36593     },
36594     
36595     reset : function()
36596     {
36597         this.setDay('');
36598         this.setMonth('');
36599         this.setYear('');
36600         this.inputEl.dom.value = '';
36601         this.validate();
36602         return;
36603     },
36604     
36605     validate : function()
36606     {
36607         var d = this.dayField.validate();
36608         var m = this.monthField.validate();
36609         var y = this.yearField.validate();
36610         
36611         var valid = true;
36612         
36613         if(
36614                 (!this.dayAllowBlank && !d) ||
36615                 (!this.monthAllowBlank && !m) ||
36616                 (!this.yearAllowBlank && !y)
36617         ){
36618             valid = false;
36619         }
36620         
36621         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
36622             return valid;
36623         }
36624         
36625         if(valid){
36626             this.markValid();
36627             return valid;
36628         }
36629         
36630         this.markInvalid();
36631         
36632         return valid;
36633     },
36634     
36635     markValid : function()
36636     {
36637         
36638         var label = this.el.select('label', true).first();
36639         var icon = this.el.select('i.fa-star', true).first();
36640
36641         if(label && icon){
36642             icon.remove();
36643         }
36644         
36645         this.fireEvent('valid', this);
36646     },
36647     
36648      /**
36649      * Mark this field as invalid
36650      * @param {String} msg The validation message
36651      */
36652     markInvalid : function(msg)
36653     {
36654         
36655         var label = this.el.select('label', true).first();
36656         var icon = this.el.select('i.fa-star', true).first();
36657
36658         if(label && !icon){
36659             this.el.select('.roo-date-split-field-label', true).createChild({
36660                 tag : 'i',
36661                 cls : 'text-danger fa fa-lg fa-star',
36662                 tooltip : 'This field is required',
36663                 style : 'margin-right:5px;'
36664             }, label, true);
36665         }
36666         
36667         this.fireEvent('invalid', this, msg);
36668     },
36669     
36670     clearInvalid : function()
36671     {
36672         var label = this.el.select('label', true).first();
36673         var icon = this.el.select('i.fa-star', true).first();
36674
36675         if(label && icon){
36676             icon.remove();
36677         }
36678         
36679         this.fireEvent('valid', this);
36680     },
36681     
36682     getName: function()
36683     {
36684         return this.name;
36685     }
36686     
36687 });
36688
36689  
36690
36691 /**
36692  * @class Roo.bootstrap.LayoutMasonry
36693  * @extends Roo.bootstrap.Component
36694  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
36695  * Bootstrap Layout Masonry class
36696  *
36697  * This is based on 
36698  * http://masonry.desandro.com
36699  *
36700  * The idea is to render all the bricks based on vertical width...
36701  *
36702  * The original code extends 'outlayer' - we might need to use that....
36703
36704  * @constructor
36705  * Create a new Element
36706  * @param {Object} config The config object
36707  */
36708
36709 Roo.bootstrap.LayoutMasonry = function(config){
36710     
36711     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
36712     
36713     this.bricks = [];
36714     
36715     Roo.bootstrap.LayoutMasonry.register(this);
36716     
36717     this.addEvents({
36718         // raw events
36719         /**
36720          * @event layout
36721          * Fire after layout the items
36722          * @param {Roo.bootstrap.LayoutMasonry} this
36723          * @param {Roo.EventObject} e
36724          */
36725         "layout" : true
36726     });
36727     
36728 };
36729
36730 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
36731     
36732     /**
36733      * @cfg {Boolean} isLayoutInstant = no animation?
36734      */   
36735     isLayoutInstant : false, // needed?
36736    
36737     /**
36738      * @cfg {Number} boxWidth  width of the columns
36739      */   
36740     boxWidth : 450,
36741     
36742       /**
36743      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
36744      */   
36745     boxHeight : 0,
36746     
36747     /**
36748      * @cfg {Number} padWidth padding below box..
36749      */   
36750     padWidth : 10, 
36751     
36752     /**
36753      * @cfg {Number} gutter gutter width..
36754      */   
36755     gutter : 10,
36756     
36757      /**
36758      * @cfg {Number} maxCols maximum number of columns
36759      */   
36760     
36761     maxCols: 0,
36762     
36763     /**
36764      * @cfg {Boolean} isAutoInitial defalut true
36765      */   
36766     isAutoInitial : true, 
36767     
36768     containerWidth: 0,
36769     
36770     /**
36771      * @cfg {Boolean} isHorizontal defalut false
36772      */   
36773     isHorizontal : false, 
36774
36775     currentSize : null,
36776     
36777     tag: 'div',
36778     
36779     cls: '',
36780     
36781     bricks: null, //CompositeElement
36782     
36783     cols : 1,
36784     
36785     _isLayoutInited : false,
36786     
36787 //    isAlternative : false, // only use for vertical layout...
36788     
36789     /**
36790      * @cfg {Number} alternativePadWidth padding below box..
36791      */   
36792     alternativePadWidth : 50,
36793     
36794     selectedBrick : [],
36795     
36796     getAutoCreate : function(){
36797         
36798         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
36799         
36800         var cfg = {
36801             tag: this.tag,
36802             cls: 'blog-masonary-wrapper ' + this.cls,
36803             cn : {
36804                 cls : 'mas-boxes masonary'
36805             }
36806         };
36807         
36808         return cfg;
36809     },
36810     
36811     getChildContainer: function( )
36812     {
36813         if (this.boxesEl) {
36814             return this.boxesEl;
36815         }
36816         
36817         this.boxesEl = this.el.select('.mas-boxes').first();
36818         
36819         return this.boxesEl;
36820     },
36821     
36822     
36823     initEvents : function()
36824     {
36825         var _this = this;
36826         
36827         if(this.isAutoInitial){
36828             Roo.log('hook children rendered');
36829             this.on('childrenrendered', function() {
36830                 Roo.log('children rendered');
36831                 _this.initial();
36832             } ,this);
36833         }
36834     },
36835     
36836     initial : function()
36837     {
36838         this.selectedBrick = [];
36839         
36840         this.currentSize = this.el.getBox(true);
36841         
36842         Roo.EventManager.onWindowResize(this.resize, this); 
36843
36844         if(!this.isAutoInitial){
36845             this.layout();
36846             return;
36847         }
36848         
36849         this.layout();
36850         
36851         return;
36852         //this.layout.defer(500,this);
36853         
36854     },
36855     
36856     resize : function()
36857     {
36858         var cs = this.el.getBox(true);
36859         
36860         if (
36861                 this.currentSize.width == cs.width && 
36862                 this.currentSize.x == cs.x && 
36863                 this.currentSize.height == cs.height && 
36864                 this.currentSize.y == cs.y 
36865         ) {
36866             Roo.log("no change in with or X or Y");
36867             return;
36868         }
36869         
36870         this.currentSize = cs;
36871         
36872         this.layout();
36873         
36874     },
36875     
36876     layout : function()
36877     {   
36878         this._resetLayout();
36879         
36880         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
36881         
36882         this.layoutItems( isInstant );
36883       
36884         this._isLayoutInited = true;
36885         
36886         this.fireEvent('layout', this);
36887         
36888     },
36889     
36890     _resetLayout : function()
36891     {
36892         if(this.isHorizontal){
36893             this.horizontalMeasureColumns();
36894             return;
36895         }
36896         
36897         this.verticalMeasureColumns();
36898         
36899     },
36900     
36901     verticalMeasureColumns : function()
36902     {
36903         this.getContainerWidth();
36904         
36905 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
36906 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
36907 //            return;
36908 //        }
36909         
36910         var boxWidth = this.boxWidth + this.padWidth;
36911         
36912         if(this.containerWidth < this.boxWidth){
36913             boxWidth = this.containerWidth
36914         }
36915         
36916         var containerWidth = this.containerWidth;
36917         
36918         var cols = Math.floor(containerWidth / boxWidth);
36919         
36920         this.cols = Math.max( cols, 1 );
36921         
36922         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36923         
36924         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
36925         
36926         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
36927         
36928         this.colWidth = boxWidth + avail - this.padWidth;
36929         
36930         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
36931         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
36932     },
36933     
36934     horizontalMeasureColumns : function()
36935     {
36936         this.getContainerWidth();
36937         
36938         var boxWidth = this.boxWidth;
36939         
36940         if(this.containerWidth < boxWidth){
36941             boxWidth = this.containerWidth;
36942         }
36943         
36944         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
36945         
36946         this.el.setHeight(boxWidth);
36947         
36948     },
36949     
36950     getContainerWidth : function()
36951     {
36952         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36953     },
36954     
36955     layoutItems : function( isInstant )
36956     {
36957         Roo.log(this.bricks);
36958         
36959         var items = Roo.apply([], this.bricks);
36960         
36961         if(this.isHorizontal){
36962             this._horizontalLayoutItems( items , isInstant );
36963             return;
36964         }
36965         
36966 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
36967 //            this._verticalAlternativeLayoutItems( items , isInstant );
36968 //            return;
36969 //        }
36970         
36971         this._verticalLayoutItems( items , isInstant );
36972         
36973     },
36974     
36975     _verticalLayoutItems : function ( items , isInstant)
36976     {
36977         if ( !items || !items.length ) {
36978             return;
36979         }
36980         
36981         var standard = [
36982             ['xs', 'xs', 'xs', 'tall'],
36983             ['xs', 'xs', 'tall'],
36984             ['xs', 'xs', 'sm'],
36985             ['xs', 'xs', 'xs'],
36986             ['xs', 'tall'],
36987             ['xs', 'sm'],
36988             ['xs', 'xs'],
36989             ['xs'],
36990             
36991             ['sm', 'xs', 'xs'],
36992             ['sm', 'xs'],
36993             ['sm'],
36994             
36995             ['tall', 'xs', 'xs', 'xs'],
36996             ['tall', 'xs', 'xs'],
36997             ['tall', 'xs'],
36998             ['tall']
36999             
37000         ];
37001         
37002         var queue = [];
37003         
37004         var boxes = [];
37005         
37006         var box = [];
37007         
37008         Roo.each(items, function(item, k){
37009             
37010             switch (item.size) {
37011                 // these layouts take up a full box,
37012                 case 'md' :
37013                 case 'md-left' :
37014                 case 'md-right' :
37015                 case 'wide' :
37016                     
37017                     if(box.length){
37018                         boxes.push(box);
37019                         box = [];
37020                     }
37021                     
37022                     boxes.push([item]);
37023                     
37024                     break;
37025                     
37026                 case 'xs' :
37027                 case 'sm' :
37028                 case 'tall' :
37029                     
37030                     box.push(item);
37031                     
37032                     break;
37033                 default :
37034                     break;
37035                     
37036             }
37037             
37038         }, this);
37039         
37040         if(box.length){
37041             boxes.push(box);
37042             box = [];
37043         }
37044         
37045         var filterPattern = function(box, length)
37046         {
37047             if(!box.length){
37048                 return;
37049             }
37050             
37051             var match = false;
37052             
37053             var pattern = box.slice(0, length);
37054             
37055             var format = [];
37056             
37057             Roo.each(pattern, function(i){
37058                 format.push(i.size);
37059             }, this);
37060             
37061             Roo.each(standard, function(s){
37062                 
37063                 if(String(s) != String(format)){
37064                     return;
37065                 }
37066                 
37067                 match = true;
37068                 return false;
37069                 
37070             }, this);
37071             
37072             if(!match && length == 1){
37073                 return;
37074             }
37075             
37076             if(!match){
37077                 filterPattern(box, length - 1);
37078                 return;
37079             }
37080                 
37081             queue.push(pattern);
37082
37083             box = box.slice(length, box.length);
37084
37085             filterPattern(box, 4);
37086
37087             return;
37088             
37089         }
37090         
37091         Roo.each(boxes, function(box, k){
37092             
37093             if(!box.length){
37094                 return;
37095             }
37096             
37097             if(box.length == 1){
37098                 queue.push(box);
37099                 return;
37100             }
37101             
37102             filterPattern(box, 4);
37103             
37104         }, this);
37105         
37106         this._processVerticalLayoutQueue( queue, isInstant );
37107         
37108     },
37109     
37110 //    _verticalAlternativeLayoutItems : function( items , isInstant )
37111 //    {
37112 //        if ( !items || !items.length ) {
37113 //            return;
37114 //        }
37115 //
37116 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
37117 //        
37118 //    },
37119     
37120     _horizontalLayoutItems : function ( items , isInstant)
37121     {
37122         if ( !items || !items.length || items.length < 3) {
37123             return;
37124         }
37125         
37126         items.reverse();
37127         
37128         var eItems = items.slice(0, 3);
37129         
37130         items = items.slice(3, items.length);
37131         
37132         var standard = [
37133             ['xs', 'xs', 'xs', 'wide'],
37134             ['xs', 'xs', 'wide'],
37135             ['xs', 'xs', 'sm'],
37136             ['xs', 'xs', 'xs'],
37137             ['xs', 'wide'],
37138             ['xs', 'sm'],
37139             ['xs', 'xs'],
37140             ['xs'],
37141             
37142             ['sm', 'xs', 'xs'],
37143             ['sm', 'xs'],
37144             ['sm'],
37145             
37146             ['wide', 'xs', 'xs', 'xs'],
37147             ['wide', 'xs', 'xs'],
37148             ['wide', 'xs'],
37149             ['wide'],
37150             
37151             ['wide-thin']
37152         ];
37153         
37154         var queue = [];
37155         
37156         var boxes = [];
37157         
37158         var box = [];
37159         
37160         Roo.each(items, function(item, k){
37161             
37162             switch (item.size) {
37163                 case 'md' :
37164                 case 'md-left' :
37165                 case 'md-right' :
37166                 case 'tall' :
37167                     
37168                     if(box.length){
37169                         boxes.push(box);
37170                         box = [];
37171                     }
37172                     
37173                     boxes.push([item]);
37174                     
37175                     break;
37176                     
37177                 case 'xs' :
37178                 case 'sm' :
37179                 case 'wide' :
37180                 case 'wide-thin' :
37181                     
37182                     box.push(item);
37183                     
37184                     break;
37185                 default :
37186                     break;
37187                     
37188             }
37189             
37190         }, this);
37191         
37192         if(box.length){
37193             boxes.push(box);
37194             box = [];
37195         }
37196         
37197         var filterPattern = function(box, length)
37198         {
37199             if(!box.length){
37200                 return;
37201             }
37202             
37203             var match = false;
37204             
37205             var pattern = box.slice(0, length);
37206             
37207             var format = [];
37208             
37209             Roo.each(pattern, function(i){
37210                 format.push(i.size);
37211             }, this);
37212             
37213             Roo.each(standard, function(s){
37214                 
37215                 if(String(s) != String(format)){
37216                     return;
37217                 }
37218                 
37219                 match = true;
37220                 return false;
37221                 
37222             }, this);
37223             
37224             if(!match && length == 1){
37225                 return;
37226             }
37227             
37228             if(!match){
37229                 filterPattern(box, length - 1);
37230                 return;
37231             }
37232                 
37233             queue.push(pattern);
37234
37235             box = box.slice(length, box.length);
37236
37237             filterPattern(box, 4);
37238
37239             return;
37240             
37241         }
37242         
37243         Roo.each(boxes, function(box, k){
37244             
37245             if(!box.length){
37246                 return;
37247             }
37248             
37249             if(box.length == 1){
37250                 queue.push(box);
37251                 return;
37252             }
37253             
37254             filterPattern(box, 4);
37255             
37256         }, this);
37257         
37258         
37259         var prune = [];
37260         
37261         var pos = this.el.getBox(true);
37262         
37263         var minX = pos.x;
37264         
37265         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37266         
37267         var hit_end = false;
37268         
37269         Roo.each(queue, function(box){
37270             
37271             if(hit_end){
37272                 
37273                 Roo.each(box, function(b){
37274                 
37275                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
37276                     b.el.hide();
37277
37278                 }, this);
37279
37280                 return;
37281             }
37282             
37283             var mx = 0;
37284             
37285             Roo.each(box, function(b){
37286                 
37287                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
37288                 b.el.show();
37289
37290                 mx = Math.max(mx, b.x);
37291                 
37292             }, this);
37293             
37294             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
37295             
37296             if(maxX < minX){
37297                 
37298                 Roo.each(box, function(b){
37299                 
37300                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
37301                     b.el.hide();
37302                     
37303                 }, this);
37304                 
37305                 hit_end = true;
37306                 
37307                 return;
37308             }
37309             
37310             prune.push(box);
37311             
37312         }, this);
37313         
37314         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
37315     },
37316     
37317     /** Sets position of item in DOM
37318     * @param {Element} item
37319     * @param {Number} x - horizontal position
37320     * @param {Number} y - vertical position
37321     * @param {Boolean} isInstant - disables transitions
37322     */
37323     _processVerticalLayoutQueue : function( queue, isInstant )
37324     {
37325         var pos = this.el.getBox(true);
37326         var x = pos.x;
37327         var y = pos.y;
37328         var maxY = [];
37329         
37330         for (var i = 0; i < this.cols; i++){
37331             maxY[i] = pos.y;
37332         }
37333         
37334         Roo.each(queue, function(box, k){
37335             
37336             var col = k % this.cols;
37337             
37338             Roo.each(box, function(b,kk){
37339                 
37340                 b.el.position('absolute');
37341                 
37342                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37343                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37344                 
37345                 if(b.size == 'md-left' || b.size == 'md-right'){
37346                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37347                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37348                 }
37349                 
37350                 b.el.setWidth(width);
37351                 b.el.setHeight(height);
37352                 // iframe?
37353                 b.el.select('iframe',true).setSize(width,height);
37354                 
37355             }, this);
37356             
37357             for (var i = 0; i < this.cols; i++){
37358                 
37359                 if(maxY[i] < maxY[col]){
37360                     col = i;
37361                     continue;
37362                 }
37363                 
37364                 col = Math.min(col, i);
37365                 
37366             }
37367             
37368             x = pos.x + col * (this.colWidth + this.padWidth);
37369             
37370             y = maxY[col];
37371             
37372             var positions = [];
37373             
37374             switch (box.length){
37375                 case 1 :
37376                     positions = this.getVerticalOneBoxColPositions(x, y, box);
37377                     break;
37378                 case 2 :
37379                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
37380                     break;
37381                 case 3 :
37382                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
37383                     break;
37384                 case 4 :
37385                     positions = this.getVerticalFourBoxColPositions(x, y, box);
37386                     break;
37387                 default :
37388                     break;
37389             }
37390             
37391             Roo.each(box, function(b,kk){
37392                 
37393                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37394                 
37395                 var sz = b.el.getSize();
37396                 
37397                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
37398                 
37399             }, this);
37400             
37401         }, this);
37402         
37403         var mY = 0;
37404         
37405         for (var i = 0; i < this.cols; i++){
37406             mY = Math.max(mY, maxY[i]);
37407         }
37408         
37409         this.el.setHeight(mY - pos.y);
37410         
37411     },
37412     
37413 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
37414 //    {
37415 //        var pos = this.el.getBox(true);
37416 //        var x = pos.x;
37417 //        var y = pos.y;
37418 //        var maxX = pos.right;
37419 //        
37420 //        var maxHeight = 0;
37421 //        
37422 //        Roo.each(items, function(item, k){
37423 //            
37424 //            var c = k % 2;
37425 //            
37426 //            item.el.position('absolute');
37427 //                
37428 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
37429 //
37430 //            item.el.setWidth(width);
37431 //
37432 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
37433 //
37434 //            item.el.setHeight(height);
37435 //            
37436 //            if(c == 0){
37437 //                item.el.setXY([x, y], isInstant ? false : true);
37438 //            } else {
37439 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
37440 //            }
37441 //            
37442 //            y = y + height + this.alternativePadWidth;
37443 //            
37444 //            maxHeight = maxHeight + height + this.alternativePadWidth;
37445 //            
37446 //        }, this);
37447 //        
37448 //        this.el.setHeight(maxHeight);
37449 //        
37450 //    },
37451     
37452     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
37453     {
37454         var pos = this.el.getBox(true);
37455         
37456         var minX = pos.x;
37457         var minY = pos.y;
37458         
37459         var maxX = pos.right;
37460         
37461         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
37462         
37463         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37464         
37465         Roo.each(queue, function(box, k){
37466             
37467             Roo.each(box, function(b, kk){
37468                 
37469                 b.el.position('absolute');
37470                 
37471                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37472                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37473                 
37474                 if(b.size == 'md-left' || b.size == 'md-right'){
37475                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37476                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37477                 }
37478                 
37479                 b.el.setWidth(width);
37480                 b.el.setHeight(height);
37481                 
37482             }, this);
37483             
37484             if(!box.length){
37485                 return;
37486             }
37487             
37488             var positions = [];
37489             
37490             switch (box.length){
37491                 case 1 :
37492                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
37493                     break;
37494                 case 2 :
37495                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
37496                     break;
37497                 case 3 :
37498                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
37499                     break;
37500                 case 4 :
37501                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
37502                     break;
37503                 default :
37504                     break;
37505             }
37506             
37507             Roo.each(box, function(b,kk){
37508                 
37509                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37510                 
37511                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
37512                 
37513             }, this);
37514             
37515         }, this);
37516         
37517     },
37518     
37519     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
37520     {
37521         Roo.each(eItems, function(b,k){
37522             
37523             b.size = (k == 0) ? 'sm' : 'xs';
37524             b.x = (k == 0) ? 2 : 1;
37525             b.y = (k == 0) ? 2 : 1;
37526             
37527             b.el.position('absolute');
37528             
37529             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37530                 
37531             b.el.setWidth(width);
37532             
37533             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37534             
37535             b.el.setHeight(height);
37536             
37537         }, this);
37538
37539         var positions = [];
37540         
37541         positions.push({
37542             x : maxX - this.unitWidth * 2 - this.gutter,
37543             y : minY
37544         });
37545         
37546         positions.push({
37547             x : maxX - this.unitWidth,
37548             y : minY + (this.unitWidth + this.gutter) * 2
37549         });
37550         
37551         positions.push({
37552             x : maxX - this.unitWidth * 3 - this.gutter * 2,
37553             y : minY
37554         });
37555         
37556         Roo.each(eItems, function(b,k){
37557             
37558             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
37559
37560         }, this);
37561         
37562     },
37563     
37564     getVerticalOneBoxColPositions : function(x, y, box)
37565     {
37566         var pos = [];
37567         
37568         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
37569         
37570         if(box[0].size == 'md-left'){
37571             rand = 0;
37572         }
37573         
37574         if(box[0].size == 'md-right'){
37575             rand = 1;
37576         }
37577         
37578         pos.push({
37579             x : x + (this.unitWidth + this.gutter) * rand,
37580             y : y
37581         });
37582         
37583         return pos;
37584     },
37585     
37586     getVerticalTwoBoxColPositions : function(x, y, box)
37587     {
37588         var pos = [];
37589         
37590         if(box[0].size == 'xs'){
37591             
37592             pos.push({
37593                 x : x,
37594                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
37595             });
37596
37597             pos.push({
37598                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
37599                 y : y
37600             });
37601             
37602             return pos;
37603             
37604         }
37605         
37606         pos.push({
37607             x : x,
37608             y : y
37609         });
37610
37611         pos.push({
37612             x : x + (this.unitWidth + this.gutter) * 2,
37613             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
37614         });
37615         
37616         return pos;
37617         
37618     },
37619     
37620     getVerticalThreeBoxColPositions : function(x, y, box)
37621     {
37622         var pos = [];
37623         
37624         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
37625             
37626             pos.push({
37627                 x : x,
37628                 y : y
37629             });
37630
37631             pos.push({
37632                 x : x + (this.unitWidth + this.gutter) * 1,
37633                 y : y
37634             });
37635             
37636             pos.push({
37637                 x : x + (this.unitWidth + this.gutter) * 2,
37638                 y : y
37639             });
37640             
37641             return pos;
37642             
37643         }
37644         
37645         if(box[0].size == 'xs' && box[1].size == 'xs'){
37646             
37647             pos.push({
37648                 x : x,
37649                 y : y
37650             });
37651
37652             pos.push({
37653                 x : x,
37654                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
37655             });
37656             
37657             pos.push({
37658                 x : x + (this.unitWidth + this.gutter) * 1,
37659                 y : y
37660             });
37661             
37662             return pos;
37663             
37664         }
37665         
37666         pos.push({
37667             x : x,
37668             y : y
37669         });
37670
37671         pos.push({
37672             x : x + (this.unitWidth + this.gutter) * 2,
37673             y : y
37674         });
37675
37676         pos.push({
37677             x : x + (this.unitWidth + this.gutter) * 2,
37678             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
37679         });
37680             
37681         return pos;
37682         
37683     },
37684     
37685     getVerticalFourBoxColPositions : function(x, y, box)
37686     {
37687         var pos = [];
37688         
37689         if(box[0].size == 'xs'){
37690             
37691             pos.push({
37692                 x : x,
37693                 y : y
37694             });
37695
37696             pos.push({
37697                 x : x,
37698                 y : y + (this.unitHeight + this.gutter) * 1
37699             });
37700             
37701             pos.push({
37702                 x : x,
37703                 y : y + (this.unitHeight + this.gutter) * 2
37704             });
37705             
37706             pos.push({
37707                 x : x + (this.unitWidth + this.gutter) * 1,
37708                 y : y
37709             });
37710             
37711             return pos;
37712             
37713         }
37714         
37715         pos.push({
37716             x : x,
37717             y : y
37718         });
37719
37720         pos.push({
37721             x : x + (this.unitWidth + this.gutter) * 2,
37722             y : y
37723         });
37724
37725         pos.push({
37726             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
37727             y : y + (this.unitHeight + this.gutter) * 1
37728         });
37729
37730         pos.push({
37731             x : x + (this.unitWidth + this.gutter) * 2,
37732             y : y + (this.unitWidth + this.gutter) * 2
37733         });
37734
37735         return pos;
37736         
37737     },
37738     
37739     getHorizontalOneBoxColPositions : function(maxX, minY, box)
37740     {
37741         var pos = [];
37742         
37743         if(box[0].size == 'md-left'){
37744             pos.push({
37745                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37746                 y : minY
37747             });
37748             
37749             return pos;
37750         }
37751         
37752         if(box[0].size == 'md-right'){
37753             pos.push({
37754                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37755                 y : minY + (this.unitWidth + this.gutter) * 1
37756             });
37757             
37758             return pos;
37759         }
37760         
37761         var rand = Math.floor(Math.random() * (4 - box[0].y));
37762         
37763         pos.push({
37764             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37765             y : minY + (this.unitWidth + this.gutter) * rand
37766         });
37767         
37768         return pos;
37769         
37770     },
37771     
37772     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
37773     {
37774         var pos = [];
37775         
37776         if(box[0].size == 'xs'){
37777             
37778             pos.push({
37779                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37780                 y : minY
37781             });
37782
37783             pos.push({
37784                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37785                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
37786             });
37787             
37788             return pos;
37789             
37790         }
37791         
37792         pos.push({
37793             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37794             y : minY
37795         });
37796
37797         pos.push({
37798             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37799             y : minY + (this.unitWidth + this.gutter) * 2
37800         });
37801         
37802         return pos;
37803         
37804     },
37805     
37806     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
37807     {
37808         var pos = [];
37809         
37810         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
37811             
37812             pos.push({
37813                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37814                 y : minY
37815             });
37816
37817             pos.push({
37818                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37819                 y : minY + (this.unitWidth + this.gutter) * 1
37820             });
37821             
37822             pos.push({
37823                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37824                 y : minY + (this.unitWidth + this.gutter) * 2
37825             });
37826             
37827             return pos;
37828             
37829         }
37830         
37831         if(box[0].size == 'xs' && box[1].size == 'xs'){
37832             
37833             pos.push({
37834                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37835                 y : minY
37836             });
37837
37838             pos.push({
37839                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37840                 y : minY
37841             });
37842             
37843             pos.push({
37844                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37845                 y : minY + (this.unitWidth + this.gutter) * 1
37846             });
37847             
37848             return pos;
37849             
37850         }
37851         
37852         pos.push({
37853             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37854             y : minY
37855         });
37856
37857         pos.push({
37858             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37859             y : minY + (this.unitWidth + this.gutter) * 2
37860         });
37861
37862         pos.push({
37863             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37864             y : minY + (this.unitWidth + this.gutter) * 2
37865         });
37866             
37867         return pos;
37868         
37869     },
37870     
37871     getHorizontalFourBoxColPositions : function(maxX, minY, box)
37872     {
37873         var pos = [];
37874         
37875         if(box[0].size == 'xs'){
37876             
37877             pos.push({
37878                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37879                 y : minY
37880             });
37881
37882             pos.push({
37883                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37884                 y : minY
37885             });
37886             
37887             pos.push({
37888                 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),
37889                 y : minY
37890             });
37891             
37892             pos.push({
37893                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
37894                 y : minY + (this.unitWidth + this.gutter) * 1
37895             });
37896             
37897             return pos;
37898             
37899         }
37900         
37901         pos.push({
37902             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37903             y : minY
37904         });
37905         
37906         pos.push({
37907             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37908             y : minY + (this.unitWidth + this.gutter) * 2
37909         });
37910         
37911         pos.push({
37912             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37913             y : minY + (this.unitWidth + this.gutter) * 2
37914         });
37915         
37916         pos.push({
37917             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),
37918             y : minY + (this.unitWidth + this.gutter) * 2
37919         });
37920
37921         return pos;
37922         
37923     },
37924     
37925     /**
37926     * remove a Masonry Brick
37927     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
37928     */
37929     removeBrick : function(brick_id)
37930     {
37931         if (!brick_id) {
37932             return;
37933         }
37934         
37935         for (var i = 0; i<this.bricks.length; i++) {
37936             if (this.bricks[i].id == brick_id) {
37937                 this.bricks.splice(i,1);
37938                 this.el.dom.removeChild(Roo.get(brick_id).dom);
37939                 this.initial();
37940             }
37941         }
37942     },
37943     
37944     /**
37945     * adds a Masonry Brick
37946     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
37947     */
37948     addBrick : function(cfg)
37949     {
37950         var cn = new Roo.bootstrap.MasonryBrick(cfg);
37951         //this.register(cn);
37952         cn.parentId = this.id;
37953         cn.render(this.el);
37954         return cn;
37955     },
37956     
37957     /**
37958     * register a Masonry Brick
37959     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
37960     */
37961     
37962     register : function(brick)
37963     {
37964         this.bricks.push(brick);
37965         brick.masonryId = this.id;
37966     },
37967     
37968     /**
37969     * clear all the Masonry Brick
37970     */
37971     clearAll : function()
37972     {
37973         this.bricks = [];
37974         //this.getChildContainer().dom.innerHTML = "";
37975         this.el.dom.innerHTML = '';
37976     },
37977     
37978     getSelected : function()
37979     {
37980         if (!this.selectedBrick) {
37981             return false;
37982         }
37983         
37984         return this.selectedBrick;
37985     }
37986 });
37987
37988 Roo.apply(Roo.bootstrap.LayoutMasonry, {
37989     
37990     groups: {},
37991      /**
37992     * register a Masonry Layout
37993     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
37994     */
37995     
37996     register : function(layout)
37997     {
37998         this.groups[layout.id] = layout;
37999     },
38000     /**
38001     * fetch a  Masonry Layout based on the masonry layout ID
38002     * @param {string} the masonry layout to add
38003     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
38004     */
38005     
38006     get: function(layout_id) {
38007         if (typeof(this.groups[layout_id]) == 'undefined') {
38008             return false;
38009         }
38010         return this.groups[layout_id] ;
38011     }
38012     
38013     
38014     
38015 });
38016
38017  
38018
38019  /**
38020  *
38021  * This is based on 
38022  * http://masonry.desandro.com
38023  *
38024  * The idea is to render all the bricks based on vertical width...
38025  *
38026  * The original code extends 'outlayer' - we might need to use that....
38027  * 
38028  */
38029
38030
38031 /**
38032  * @class Roo.bootstrap.LayoutMasonryAuto
38033  * @extends Roo.bootstrap.Component
38034  * Bootstrap Layout Masonry class
38035  * 
38036  * @constructor
38037  * Create a new Element
38038  * @param {Object} config The config object
38039  */
38040
38041 Roo.bootstrap.LayoutMasonryAuto = function(config){
38042     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
38043 };
38044
38045 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
38046     
38047       /**
38048      * @cfg {Boolean} isFitWidth  - resize the width..
38049      */   
38050     isFitWidth : false,  // options..
38051     /**
38052      * @cfg {Boolean} isOriginLeft = left align?
38053      */   
38054     isOriginLeft : true,
38055     /**
38056      * @cfg {Boolean} isOriginTop = top align?
38057      */   
38058     isOriginTop : false,
38059     /**
38060      * @cfg {Boolean} isLayoutInstant = no animation?
38061      */   
38062     isLayoutInstant : false, // needed?
38063     /**
38064      * @cfg {Boolean} isResizingContainer = not sure if this is used..
38065      */   
38066     isResizingContainer : true,
38067     /**
38068      * @cfg {Number} columnWidth  width of the columns 
38069      */   
38070     
38071     columnWidth : 0,
38072     
38073     /**
38074      * @cfg {Number} maxCols maximum number of columns
38075      */   
38076     
38077     maxCols: 0,
38078     /**
38079      * @cfg {Number} padHeight padding below box..
38080      */   
38081     
38082     padHeight : 10, 
38083     
38084     /**
38085      * @cfg {Boolean} isAutoInitial defalut true
38086      */   
38087     
38088     isAutoInitial : true, 
38089     
38090     // private?
38091     gutter : 0,
38092     
38093     containerWidth: 0,
38094     initialColumnWidth : 0,
38095     currentSize : null,
38096     
38097     colYs : null, // array.
38098     maxY : 0,
38099     padWidth: 10,
38100     
38101     
38102     tag: 'div',
38103     cls: '',
38104     bricks: null, //CompositeElement
38105     cols : 0, // array?
38106     // element : null, // wrapped now this.el
38107     _isLayoutInited : null, 
38108     
38109     
38110     getAutoCreate : function(){
38111         
38112         var cfg = {
38113             tag: this.tag,
38114             cls: 'blog-masonary-wrapper ' + this.cls,
38115             cn : {
38116                 cls : 'mas-boxes masonary'
38117             }
38118         };
38119         
38120         return cfg;
38121     },
38122     
38123     getChildContainer: function( )
38124     {
38125         if (this.boxesEl) {
38126             return this.boxesEl;
38127         }
38128         
38129         this.boxesEl = this.el.select('.mas-boxes').first();
38130         
38131         return this.boxesEl;
38132     },
38133     
38134     
38135     initEvents : function()
38136     {
38137         var _this = this;
38138         
38139         if(this.isAutoInitial){
38140             Roo.log('hook children rendered');
38141             this.on('childrenrendered', function() {
38142                 Roo.log('children rendered');
38143                 _this.initial();
38144             } ,this);
38145         }
38146         
38147     },
38148     
38149     initial : function()
38150     {
38151         this.reloadItems();
38152
38153         this.currentSize = this.el.getBox(true);
38154
38155         /// was window resize... - let's see if this works..
38156         Roo.EventManager.onWindowResize(this.resize, this); 
38157
38158         if(!this.isAutoInitial){
38159             this.layout();
38160             return;
38161         }
38162         
38163         this.layout.defer(500,this);
38164     },
38165     
38166     reloadItems: function()
38167     {
38168         this.bricks = this.el.select('.masonry-brick', true);
38169         
38170         this.bricks.each(function(b) {
38171             //Roo.log(b.getSize());
38172             if (!b.attr('originalwidth')) {
38173                 b.attr('originalwidth',  b.getSize().width);
38174             }
38175             
38176         });
38177         
38178         Roo.log(this.bricks.elements.length);
38179     },
38180     
38181     resize : function()
38182     {
38183         Roo.log('resize');
38184         var cs = this.el.getBox(true);
38185         
38186         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
38187             Roo.log("no change in with or X");
38188             return;
38189         }
38190         this.currentSize = cs;
38191         this.layout();
38192     },
38193     
38194     layout : function()
38195     {
38196          Roo.log('layout');
38197         this._resetLayout();
38198         //this._manageStamps();
38199       
38200         // don't animate first layout
38201         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38202         this.layoutItems( isInstant );
38203       
38204         // flag for initalized
38205         this._isLayoutInited = true;
38206     },
38207     
38208     layoutItems : function( isInstant )
38209     {
38210         //var items = this._getItemsForLayout( this.items );
38211         // original code supports filtering layout items.. we just ignore it..
38212         
38213         this._layoutItems( this.bricks , isInstant );
38214       
38215         this._postLayout();
38216     },
38217     _layoutItems : function ( items , isInstant)
38218     {
38219        //this.fireEvent( 'layout', this, items );
38220     
38221
38222         if ( !items || !items.elements.length ) {
38223           // no items, emit event with empty array
38224             return;
38225         }
38226
38227         var queue = [];
38228         items.each(function(item) {
38229             Roo.log("layout item");
38230             Roo.log(item);
38231             // get x/y object from method
38232             var position = this._getItemLayoutPosition( item );
38233             // enqueue
38234             position.item = item;
38235             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
38236             queue.push( position );
38237         }, this);
38238       
38239         this._processLayoutQueue( queue );
38240     },
38241     /** Sets position of item in DOM
38242     * @param {Element} item
38243     * @param {Number} x - horizontal position
38244     * @param {Number} y - vertical position
38245     * @param {Boolean} isInstant - disables transitions
38246     */
38247     _processLayoutQueue : function( queue )
38248     {
38249         for ( var i=0, len = queue.length; i < len; i++ ) {
38250             var obj = queue[i];
38251             obj.item.position('absolute');
38252             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
38253         }
38254     },
38255       
38256     
38257     /**
38258     * Any logic you want to do after each layout,
38259     * i.e. size the container
38260     */
38261     _postLayout : function()
38262     {
38263         this.resizeContainer();
38264     },
38265     
38266     resizeContainer : function()
38267     {
38268         if ( !this.isResizingContainer ) {
38269             return;
38270         }
38271         var size = this._getContainerSize();
38272         if ( size ) {
38273             this.el.setSize(size.width,size.height);
38274             this.boxesEl.setSize(size.width,size.height);
38275         }
38276     },
38277     
38278     
38279     
38280     _resetLayout : function()
38281     {
38282         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
38283         this.colWidth = this.el.getWidth();
38284         //this.gutter = this.el.getWidth(); 
38285         
38286         this.measureColumns();
38287
38288         // reset column Y
38289         var i = this.cols;
38290         this.colYs = [];
38291         while (i--) {
38292             this.colYs.push( 0 );
38293         }
38294     
38295         this.maxY = 0;
38296     },
38297
38298     measureColumns : function()
38299     {
38300         this.getContainerWidth();
38301       // if columnWidth is 0, default to outerWidth of first item
38302         if ( !this.columnWidth ) {
38303             var firstItem = this.bricks.first();
38304             Roo.log(firstItem);
38305             this.columnWidth  = this.containerWidth;
38306             if (firstItem && firstItem.attr('originalwidth') ) {
38307                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
38308             }
38309             // columnWidth fall back to item of first element
38310             Roo.log("set column width?");
38311                         this.initialColumnWidth = this.columnWidth  ;
38312
38313             // if first elem has no width, default to size of container
38314             
38315         }
38316         
38317         
38318         if (this.initialColumnWidth) {
38319             this.columnWidth = this.initialColumnWidth;
38320         }
38321         
38322         
38323             
38324         // column width is fixed at the top - however if container width get's smaller we should
38325         // reduce it...
38326         
38327         // this bit calcs how man columns..
38328             
38329         var columnWidth = this.columnWidth += this.gutter;
38330       
38331         // calculate columns
38332         var containerWidth = this.containerWidth + this.gutter;
38333         
38334         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
38335         // fix rounding errors, typically with gutters
38336         var excess = columnWidth - containerWidth % columnWidth;
38337         
38338         
38339         // if overshoot is less than a pixel, round up, otherwise floor it
38340         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
38341         cols = Math[ mathMethod ]( cols );
38342         this.cols = Math.max( cols, 1 );
38343         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38344         
38345          // padding positioning..
38346         var totalColWidth = this.cols * this.columnWidth;
38347         var padavail = this.containerWidth - totalColWidth;
38348         // so for 2 columns - we need 3 'pads'
38349         
38350         var padNeeded = (1+this.cols) * this.padWidth;
38351         
38352         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
38353         
38354         this.columnWidth += padExtra
38355         //this.padWidth = Math.floor(padavail /  ( this.cols));
38356         
38357         // adjust colum width so that padding is fixed??
38358         
38359         // we have 3 columns ... total = width * 3
38360         // we have X left over... that should be used by 
38361         
38362         //if (this.expandC) {
38363             
38364         //}
38365         
38366         
38367         
38368     },
38369     
38370     getContainerWidth : function()
38371     {
38372        /* // container is parent if fit width
38373         var container = this.isFitWidth ? this.element.parentNode : this.element;
38374         // check that this.size and size are there
38375         // IE8 triggers resize on body size change, so they might not be
38376         
38377         var size = getSize( container );  //FIXME
38378         this.containerWidth = size && size.innerWidth; //FIXME
38379         */
38380          
38381         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
38382         
38383     },
38384     
38385     _getItemLayoutPosition : function( item )  // what is item?
38386     {
38387         // we resize the item to our columnWidth..
38388       
38389         item.setWidth(this.columnWidth);
38390         item.autoBoxAdjust  = false;
38391         
38392         var sz = item.getSize();
38393  
38394         // how many columns does this brick span
38395         var remainder = this.containerWidth % this.columnWidth;
38396         
38397         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
38398         // round if off by 1 pixel, otherwise use ceil
38399         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
38400         colSpan = Math.min( colSpan, this.cols );
38401         
38402         // normally this should be '1' as we dont' currently allow multi width columns..
38403         
38404         var colGroup = this._getColGroup( colSpan );
38405         // get the minimum Y value from the columns
38406         var minimumY = Math.min.apply( Math, colGroup );
38407         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
38408         
38409         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
38410          
38411         // position the brick
38412         var position = {
38413             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
38414             y: this.currentSize.y + minimumY + this.padHeight
38415         };
38416         
38417         Roo.log(position);
38418         // apply setHeight to necessary columns
38419         var setHeight = minimumY + sz.height + this.padHeight;
38420         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
38421         
38422         var setSpan = this.cols + 1 - colGroup.length;
38423         for ( var i = 0; i < setSpan; i++ ) {
38424           this.colYs[ shortColIndex + i ] = setHeight ;
38425         }
38426       
38427         return position;
38428     },
38429     
38430     /**
38431      * @param {Number} colSpan - number of columns the element spans
38432      * @returns {Array} colGroup
38433      */
38434     _getColGroup : function( colSpan )
38435     {
38436         if ( colSpan < 2 ) {
38437           // if brick spans only one column, use all the column Ys
38438           return this.colYs;
38439         }
38440       
38441         var colGroup = [];
38442         // how many different places could this brick fit horizontally
38443         var groupCount = this.cols + 1 - colSpan;
38444         // for each group potential horizontal position
38445         for ( var i = 0; i < groupCount; i++ ) {
38446           // make an array of colY values for that one group
38447           var groupColYs = this.colYs.slice( i, i + colSpan );
38448           // and get the max value of the array
38449           colGroup[i] = Math.max.apply( Math, groupColYs );
38450         }
38451         return colGroup;
38452     },
38453     /*
38454     _manageStamp : function( stamp )
38455     {
38456         var stampSize =  stamp.getSize();
38457         var offset = stamp.getBox();
38458         // get the columns that this stamp affects
38459         var firstX = this.isOriginLeft ? offset.x : offset.right;
38460         var lastX = firstX + stampSize.width;
38461         var firstCol = Math.floor( firstX / this.columnWidth );
38462         firstCol = Math.max( 0, firstCol );
38463         
38464         var lastCol = Math.floor( lastX / this.columnWidth );
38465         // lastCol should not go over if multiple of columnWidth #425
38466         lastCol -= lastX % this.columnWidth ? 0 : 1;
38467         lastCol = Math.min( this.cols - 1, lastCol );
38468         
38469         // set colYs to bottom of the stamp
38470         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
38471             stampSize.height;
38472             
38473         for ( var i = firstCol; i <= lastCol; i++ ) {
38474           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
38475         }
38476     },
38477     */
38478     
38479     _getContainerSize : function()
38480     {
38481         this.maxY = Math.max.apply( Math, this.colYs );
38482         var size = {
38483             height: this.maxY
38484         };
38485       
38486         if ( this.isFitWidth ) {
38487             size.width = this._getContainerFitWidth();
38488         }
38489       
38490         return size;
38491     },
38492     
38493     _getContainerFitWidth : function()
38494     {
38495         var unusedCols = 0;
38496         // count unused columns
38497         var i = this.cols;
38498         while ( --i ) {
38499           if ( this.colYs[i] !== 0 ) {
38500             break;
38501           }
38502           unusedCols++;
38503         }
38504         // fit container to columns that have been used
38505         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
38506     },
38507     
38508     needsResizeLayout : function()
38509     {
38510         var previousWidth = this.containerWidth;
38511         this.getContainerWidth();
38512         return previousWidth !== this.containerWidth;
38513     }
38514  
38515 });
38516
38517  
38518
38519  /*
38520  * - LGPL
38521  *
38522  * element
38523  * 
38524  */
38525
38526 /**
38527  * @class Roo.bootstrap.MasonryBrick
38528  * @extends Roo.bootstrap.Component
38529  * Bootstrap MasonryBrick class
38530  * 
38531  * @constructor
38532  * Create a new MasonryBrick
38533  * @param {Object} config The config object
38534  */
38535
38536 Roo.bootstrap.MasonryBrick = function(config){
38537     
38538     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
38539     
38540     Roo.bootstrap.MasonryBrick.register(this);
38541     
38542     this.addEvents({
38543         // raw events
38544         /**
38545          * @event click
38546          * When a MasonryBrick is clcik
38547          * @param {Roo.bootstrap.MasonryBrick} this
38548          * @param {Roo.EventObject} e
38549          */
38550         "click" : true
38551     });
38552 };
38553
38554 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
38555     
38556     /**
38557      * @cfg {String} title
38558      */   
38559     title : '',
38560     /**
38561      * @cfg {String} html
38562      */   
38563     html : '',
38564     /**
38565      * @cfg {String} bgimage
38566      */   
38567     bgimage : '',
38568     /**
38569      * @cfg {String} videourl
38570      */   
38571     videourl : '',
38572     /**
38573      * @cfg {String} cls
38574      */   
38575     cls : '',
38576     /**
38577      * @cfg {String} href
38578      */   
38579     href : '',
38580     /**
38581      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
38582      */   
38583     size : 'xs',
38584     
38585     /**
38586      * @cfg {String} placetitle (center|bottom)
38587      */   
38588     placetitle : '',
38589     
38590     /**
38591      * @cfg {Boolean} isFitContainer defalut true
38592      */   
38593     isFitContainer : true, 
38594     
38595     /**
38596      * @cfg {Boolean} preventDefault defalut false
38597      */   
38598     preventDefault : false, 
38599     
38600     /**
38601      * @cfg {Boolean} inverse defalut false
38602      */   
38603     maskInverse : false, 
38604     
38605     getAutoCreate : function()
38606     {
38607         if(!this.isFitContainer){
38608             return this.getSplitAutoCreate();
38609         }
38610         
38611         var cls = 'masonry-brick masonry-brick-full';
38612         
38613         if(this.href.length){
38614             cls += ' masonry-brick-link';
38615         }
38616         
38617         if(this.bgimage.length){
38618             cls += ' masonry-brick-image';
38619         }
38620         
38621         if(this.maskInverse){
38622             cls += ' mask-inverse';
38623         }
38624         
38625         if(!this.html.length && !this.maskInverse && !this.videourl.length){
38626             cls += ' enable-mask';
38627         }
38628         
38629         if(this.size){
38630             cls += ' masonry-' + this.size + '-brick';
38631         }
38632         
38633         if(this.placetitle.length){
38634             
38635             switch (this.placetitle) {
38636                 case 'center' :
38637                     cls += ' masonry-center-title';
38638                     break;
38639                 case 'bottom' :
38640                     cls += ' masonry-bottom-title';
38641                     break;
38642                 default:
38643                     break;
38644             }
38645             
38646         } else {
38647             if(!this.html.length && !this.bgimage.length){
38648                 cls += ' masonry-center-title';
38649             }
38650
38651             if(!this.html.length && this.bgimage.length){
38652                 cls += ' masonry-bottom-title';
38653             }
38654         }
38655         
38656         if(this.cls){
38657             cls += ' ' + this.cls;
38658         }
38659         
38660         var cfg = {
38661             tag: (this.href.length) ? 'a' : 'div',
38662             cls: cls,
38663             cn: [
38664                 {
38665                     tag: 'div',
38666                     cls: 'masonry-brick-mask'
38667                 },
38668                 {
38669                     tag: 'div',
38670                     cls: 'masonry-brick-paragraph',
38671                     cn: []
38672                 }
38673             ]
38674         };
38675         
38676         if(this.href.length){
38677             cfg.href = this.href;
38678         }
38679         
38680         var cn = cfg.cn[1].cn;
38681         
38682         if(this.title.length){
38683             cn.push({
38684                 tag: 'h4',
38685                 cls: 'masonry-brick-title',
38686                 html: this.title
38687             });
38688         }
38689         
38690         if(this.html.length){
38691             cn.push({
38692                 tag: 'p',
38693                 cls: 'masonry-brick-text',
38694                 html: this.html
38695             });
38696         }
38697         
38698         if (!this.title.length && !this.html.length) {
38699             cfg.cn[1].cls += ' hide';
38700         }
38701         
38702         if(this.bgimage.length){
38703             cfg.cn.push({
38704                 tag: 'img',
38705                 cls: 'masonry-brick-image-view',
38706                 src: this.bgimage
38707             });
38708         }
38709         
38710         if(this.videourl.length){
38711             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
38712             // youtube support only?
38713             cfg.cn.push({
38714                 tag: 'iframe',
38715                 cls: 'masonry-brick-image-view',
38716                 src: vurl,
38717                 frameborder : 0,
38718                 allowfullscreen : true
38719             });
38720         }
38721         
38722         return cfg;
38723         
38724     },
38725     
38726     getSplitAutoCreate : function()
38727     {
38728         var cls = 'masonry-brick masonry-brick-split';
38729         
38730         if(this.href.length){
38731             cls += ' masonry-brick-link';
38732         }
38733         
38734         if(this.bgimage.length){
38735             cls += ' masonry-brick-image';
38736         }
38737         
38738         if(this.size){
38739             cls += ' masonry-' + this.size + '-brick';
38740         }
38741         
38742         switch (this.placetitle) {
38743             case 'center' :
38744                 cls += ' masonry-center-title';
38745                 break;
38746             case 'bottom' :
38747                 cls += ' masonry-bottom-title';
38748                 break;
38749             default:
38750                 if(!this.bgimage.length){
38751                     cls += ' masonry-center-title';
38752                 }
38753
38754                 if(this.bgimage.length){
38755                     cls += ' masonry-bottom-title';
38756                 }
38757                 break;
38758         }
38759         
38760         if(this.cls){
38761             cls += ' ' + this.cls;
38762         }
38763         
38764         var cfg = {
38765             tag: (this.href.length) ? 'a' : 'div',
38766             cls: cls,
38767             cn: [
38768                 {
38769                     tag: 'div',
38770                     cls: 'masonry-brick-split-head',
38771                     cn: [
38772                         {
38773                             tag: 'div',
38774                             cls: 'masonry-brick-paragraph',
38775                             cn: []
38776                         }
38777                     ]
38778                 },
38779                 {
38780                     tag: 'div',
38781                     cls: 'masonry-brick-split-body',
38782                     cn: []
38783                 }
38784             ]
38785         };
38786         
38787         if(this.href.length){
38788             cfg.href = this.href;
38789         }
38790         
38791         if(this.title.length){
38792             cfg.cn[0].cn[0].cn.push({
38793                 tag: 'h4',
38794                 cls: 'masonry-brick-title',
38795                 html: this.title
38796             });
38797         }
38798         
38799         if(this.html.length){
38800             cfg.cn[1].cn.push({
38801                 tag: 'p',
38802                 cls: 'masonry-brick-text',
38803                 html: this.html
38804             });
38805         }
38806
38807         if(this.bgimage.length){
38808             cfg.cn[0].cn.push({
38809                 tag: 'img',
38810                 cls: 'masonry-brick-image-view',
38811                 src: this.bgimage
38812             });
38813         }
38814         
38815         if(this.videourl.length){
38816             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
38817             // youtube support only?
38818             cfg.cn[0].cn.cn.push({
38819                 tag: 'iframe',
38820                 cls: 'masonry-brick-image-view',
38821                 src: vurl,
38822                 frameborder : 0,
38823                 allowfullscreen : true
38824             });
38825         }
38826         
38827         return cfg;
38828     },
38829     
38830     initEvents: function() 
38831     {
38832         switch (this.size) {
38833             case 'xs' :
38834                 this.x = 1;
38835                 this.y = 1;
38836                 break;
38837             case 'sm' :
38838                 this.x = 2;
38839                 this.y = 2;
38840                 break;
38841             case 'md' :
38842             case 'md-left' :
38843             case 'md-right' :
38844                 this.x = 3;
38845                 this.y = 3;
38846                 break;
38847             case 'tall' :
38848                 this.x = 2;
38849                 this.y = 3;
38850                 break;
38851             case 'wide' :
38852                 this.x = 3;
38853                 this.y = 2;
38854                 break;
38855             case 'wide-thin' :
38856                 this.x = 3;
38857                 this.y = 1;
38858                 break;
38859                         
38860             default :
38861                 break;
38862         }
38863         
38864         if(Roo.isTouch){
38865             this.el.on('touchstart', this.onTouchStart, this);
38866             this.el.on('touchmove', this.onTouchMove, this);
38867             this.el.on('touchend', this.onTouchEnd, this);
38868             this.el.on('contextmenu', this.onContextMenu, this);
38869         } else {
38870             this.el.on('mouseenter'  ,this.enter, this);
38871             this.el.on('mouseleave', this.leave, this);
38872             this.el.on('click', this.onClick, this);
38873         }
38874         
38875         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
38876             this.parent().bricks.push(this);   
38877         }
38878         
38879     },
38880     
38881     onClick: function(e, el)
38882     {
38883         var time = this.endTimer - this.startTimer;
38884         // Roo.log(e.preventDefault());
38885         if(Roo.isTouch){
38886             if(time > 1000){
38887                 e.preventDefault();
38888                 return;
38889             }
38890         }
38891         
38892         if(!this.preventDefault){
38893             return;
38894         }
38895         
38896         e.preventDefault();
38897         
38898         if (this.activeClass != '') {
38899             this.selectBrick();
38900         }
38901         
38902         this.fireEvent('click', this, e);
38903     },
38904     
38905     enter: function(e, el)
38906     {
38907         e.preventDefault();
38908         
38909         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
38910             return;
38911         }
38912         
38913         if(this.bgimage.length && this.html.length){
38914             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
38915         }
38916     },
38917     
38918     leave: function(e, el)
38919     {
38920         e.preventDefault();
38921         
38922         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
38923             return;
38924         }
38925         
38926         if(this.bgimage.length && this.html.length){
38927             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
38928         }
38929     },
38930     
38931     onTouchStart: function(e, el)
38932     {
38933 //        e.preventDefault();
38934         
38935         this.touchmoved = false;
38936         
38937         if(!this.isFitContainer){
38938             return;
38939         }
38940         
38941         if(!this.bgimage.length || !this.html.length){
38942             return;
38943         }
38944         
38945         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
38946         
38947         this.timer = new Date().getTime();
38948         
38949     },
38950     
38951     onTouchMove: function(e, el)
38952     {
38953         this.touchmoved = true;
38954     },
38955     
38956     onContextMenu : function(e,el)
38957     {
38958         e.preventDefault();
38959         e.stopPropagation();
38960         return false;
38961     },
38962     
38963     onTouchEnd: function(e, el)
38964     {
38965 //        e.preventDefault();
38966         
38967         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
38968         
38969             this.leave(e,el);
38970             
38971             return;
38972         }
38973         
38974         if(!this.bgimage.length || !this.html.length){
38975             
38976             if(this.href.length){
38977                 window.location.href = this.href;
38978             }
38979             
38980             return;
38981         }
38982         
38983         if(!this.isFitContainer){
38984             return;
38985         }
38986         
38987         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
38988         
38989         window.location.href = this.href;
38990     },
38991     
38992     //selection on single brick only
38993     selectBrick : function() {
38994         
38995         if (!this.parentId) {
38996             return;
38997         }
38998         
38999         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
39000         var index = m.selectedBrick.indexOf(this.id);
39001         
39002         if ( index > -1) {
39003             m.selectedBrick.splice(index,1);
39004             this.el.removeClass(this.activeClass);
39005             return;
39006         }
39007         
39008         for(var i = 0; i < m.selectedBrick.length; i++) {
39009             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
39010             b.el.removeClass(b.activeClass);
39011         }
39012         
39013         m.selectedBrick = [];
39014         
39015         m.selectedBrick.push(this.id);
39016         this.el.addClass(this.activeClass);
39017         return;
39018     },
39019     
39020     isSelected : function(){
39021         return this.el.hasClass(this.activeClass);
39022         
39023     }
39024 });
39025
39026 Roo.apply(Roo.bootstrap.MasonryBrick, {
39027     
39028     //groups: {},
39029     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
39030      /**
39031     * register a Masonry Brick
39032     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39033     */
39034     
39035     register : function(brick)
39036     {
39037         //this.groups[brick.id] = brick;
39038         this.groups.add(brick.id, brick);
39039     },
39040     /**
39041     * fetch a  masonry brick based on the masonry brick ID
39042     * @param {string} the masonry brick to add
39043     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
39044     */
39045     
39046     get: function(brick_id) 
39047     {
39048         // if (typeof(this.groups[brick_id]) == 'undefined') {
39049         //     return false;
39050         // }
39051         // return this.groups[brick_id] ;
39052         
39053         if(this.groups.key(brick_id)) {
39054             return this.groups.key(brick_id);
39055         }
39056         
39057         return false;
39058     }
39059     
39060     
39061     
39062 });
39063
39064  /*
39065  * - LGPL
39066  *
39067  * element
39068  * 
39069  */
39070
39071 /**
39072  * @class Roo.bootstrap.Brick
39073  * @extends Roo.bootstrap.Component
39074  * Bootstrap Brick class
39075  * 
39076  * @constructor
39077  * Create a new Brick
39078  * @param {Object} config The config object
39079  */
39080
39081 Roo.bootstrap.Brick = function(config){
39082     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
39083     
39084     this.addEvents({
39085         // raw events
39086         /**
39087          * @event click
39088          * When a Brick is click
39089          * @param {Roo.bootstrap.Brick} this
39090          * @param {Roo.EventObject} e
39091          */
39092         "click" : true
39093     });
39094 };
39095
39096 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
39097     
39098     /**
39099      * @cfg {String} title
39100      */   
39101     title : '',
39102     /**
39103      * @cfg {String} html
39104      */   
39105     html : '',
39106     /**
39107      * @cfg {String} bgimage
39108      */   
39109     bgimage : '',
39110     /**
39111      * @cfg {String} cls
39112      */   
39113     cls : '',
39114     /**
39115      * @cfg {String} href
39116      */   
39117     href : '',
39118     /**
39119      * @cfg {String} video
39120      */   
39121     video : '',
39122     /**
39123      * @cfg {Boolean} square
39124      */   
39125     square : true,
39126     
39127     getAutoCreate : function()
39128     {
39129         var cls = 'roo-brick';
39130         
39131         if(this.href.length){
39132             cls += ' roo-brick-link';
39133         }
39134         
39135         if(this.bgimage.length){
39136             cls += ' roo-brick-image';
39137         }
39138         
39139         if(!this.html.length && !this.bgimage.length){
39140             cls += ' roo-brick-center-title';
39141         }
39142         
39143         if(!this.html.length && this.bgimage.length){
39144             cls += ' roo-brick-bottom-title';
39145         }
39146         
39147         if(this.cls){
39148             cls += ' ' + this.cls;
39149         }
39150         
39151         var cfg = {
39152             tag: (this.href.length) ? 'a' : 'div',
39153             cls: cls,
39154             cn: [
39155                 {
39156                     tag: 'div',
39157                     cls: 'roo-brick-paragraph',
39158                     cn: []
39159                 }
39160             ]
39161         };
39162         
39163         if(this.href.length){
39164             cfg.href = this.href;
39165         }
39166         
39167         var cn = cfg.cn[0].cn;
39168         
39169         if(this.title.length){
39170             cn.push({
39171                 tag: 'h4',
39172                 cls: 'roo-brick-title',
39173                 html: this.title
39174             });
39175         }
39176         
39177         if(this.html.length){
39178             cn.push({
39179                 tag: 'p',
39180                 cls: 'roo-brick-text',
39181                 html: this.html
39182             });
39183         } else {
39184             cn.cls += ' hide';
39185         }
39186         
39187         if(this.bgimage.length){
39188             cfg.cn.push({
39189                 tag: 'img',
39190                 cls: 'roo-brick-image-view',
39191                 src: this.bgimage
39192             });
39193         }
39194         
39195         return cfg;
39196     },
39197     
39198     initEvents: function() 
39199     {
39200         if(this.title.length || this.html.length){
39201             this.el.on('mouseenter'  ,this.enter, this);
39202             this.el.on('mouseleave', this.leave, this);
39203         }
39204         
39205         Roo.EventManager.onWindowResize(this.resize, this); 
39206         
39207         if(this.bgimage.length){
39208             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
39209             this.imageEl.on('load', this.onImageLoad, this);
39210             return;
39211         }
39212         
39213         this.resize();
39214     },
39215     
39216     onImageLoad : function()
39217     {
39218         this.resize();
39219     },
39220     
39221     resize : function()
39222     {
39223         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
39224         
39225         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
39226         
39227         if(this.bgimage.length){
39228             var image = this.el.select('.roo-brick-image-view', true).first();
39229             
39230             image.setWidth(paragraph.getWidth());
39231             
39232             if(this.square){
39233                 image.setHeight(paragraph.getWidth());
39234             }
39235             
39236             this.el.setHeight(image.getHeight());
39237             paragraph.setHeight(image.getHeight());
39238             
39239         }
39240         
39241     },
39242     
39243     enter: function(e, el)
39244     {
39245         e.preventDefault();
39246         
39247         if(this.bgimage.length){
39248             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
39249             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
39250         }
39251     },
39252     
39253     leave: function(e, el)
39254     {
39255         e.preventDefault();
39256         
39257         if(this.bgimage.length){
39258             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
39259             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
39260         }
39261     }
39262     
39263 });
39264
39265  
39266
39267  /*
39268  * - LGPL
39269  *
39270  * Number field 
39271  */
39272
39273 /**
39274  * @class Roo.bootstrap.form.NumberField
39275  * @extends Roo.bootstrap.form.Input
39276  * Bootstrap NumberField class
39277  * 
39278  * 
39279  * 
39280  * 
39281  * @constructor
39282  * Create a new NumberField
39283  * @param {Object} config The config object
39284  */
39285
39286 Roo.bootstrap.form.NumberField = function(config){
39287     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
39288 };
39289
39290 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
39291     
39292     /**
39293      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39294      */
39295     allowDecimals : true,
39296     /**
39297      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39298      */
39299     decimalSeparator : ".",
39300     /**
39301      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39302      */
39303     decimalPrecision : 2,
39304     /**
39305      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39306      */
39307     allowNegative : true,
39308     
39309     /**
39310      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
39311      */
39312     allowZero: true,
39313     /**
39314      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39315      */
39316     minValue : Number.NEGATIVE_INFINITY,
39317     /**
39318      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39319      */
39320     maxValue : Number.MAX_VALUE,
39321     /**
39322      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39323      */
39324     minText : "The minimum value for this field is {0}",
39325     /**
39326      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39327      */
39328     maxText : "The maximum value for this field is {0}",
39329     /**
39330      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39331      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39332      */
39333     nanText : "{0} is not a valid number",
39334     /**
39335      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
39336      */
39337     thousandsDelimiter : false,
39338     /**
39339      * @cfg {String} valueAlign alignment of value
39340      */
39341     valueAlign : "left",
39342
39343     getAutoCreate : function()
39344     {
39345         var hiddenInput = {
39346             tag: 'input',
39347             type: 'hidden',
39348             id: Roo.id(),
39349             cls: 'hidden-number-input'
39350         };
39351         
39352         if (this.name) {
39353             hiddenInput.name = this.name;
39354         }
39355         
39356         this.name = '';
39357         
39358         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
39359         
39360         this.name = hiddenInput.name;
39361         
39362         if(cfg.cn.length > 0) {
39363             cfg.cn.push(hiddenInput);
39364         }
39365         
39366         return cfg;
39367     },
39368
39369     // private
39370     initEvents : function()
39371     {   
39372         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
39373         
39374         var allowed = "0123456789";
39375         
39376         if(this.allowDecimals){
39377             allowed += this.decimalSeparator;
39378         }
39379         
39380         if(this.allowNegative){
39381             allowed += "-";
39382         }
39383         
39384         if(this.thousandsDelimiter) {
39385             allowed += ",";
39386         }
39387         
39388         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39389         
39390         var keyPress = function(e){
39391             
39392             var k = e.getKey();
39393             
39394             var c = e.getCharCode();
39395             
39396             if(
39397                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39398                     allowed.indexOf(String.fromCharCode(c)) === -1
39399             ){
39400                 e.stopEvent();
39401                 return;
39402             }
39403             
39404             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39405                 return;
39406             }
39407             
39408             if(allowed.indexOf(String.fromCharCode(c)) === -1){
39409                 e.stopEvent();
39410             }
39411         };
39412         
39413         this.el.on("keypress", keyPress, this);
39414     },
39415     
39416     validateValue : function(value)
39417     {
39418         
39419         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
39420             return false;
39421         }
39422         
39423         var num = this.parseValue(value);
39424         
39425         if(isNaN(num)){
39426             this.markInvalid(String.format(this.nanText, value));
39427             return false;
39428         }
39429         
39430         if(num < this.minValue){
39431             this.markInvalid(String.format(this.minText, this.minValue));
39432             return false;
39433         }
39434         
39435         if(num > this.maxValue){
39436             this.markInvalid(String.format(this.maxText, this.maxValue));
39437             return false;
39438         }
39439         
39440         return true;
39441     },
39442
39443     getValue : function()
39444     {
39445         var v = this.hiddenEl().getValue();
39446         
39447         return this.fixPrecision(this.parseValue(v));
39448     },
39449
39450     parseValue : function(value)
39451     {
39452         if(this.thousandsDelimiter) {
39453             value += "";
39454             r = new RegExp(",", "g");
39455             value = value.replace(r, "");
39456         }
39457         
39458         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
39459         return isNaN(value) ? '' : value;
39460     },
39461
39462     fixPrecision : function(value)
39463     {
39464         if(this.thousandsDelimiter) {
39465             value += "";
39466             r = new RegExp(",", "g");
39467             value = value.replace(r, "");
39468         }
39469         
39470         var nan = isNaN(value);
39471         
39472         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
39473             return nan ? '' : value;
39474         }
39475         return parseFloat(value).toFixed(this.decimalPrecision);
39476     },
39477
39478     setValue : function(v)
39479     {
39480         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
39481         
39482         this.value = v;
39483         
39484         if(this.rendered){
39485             
39486             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39487             
39488             this.inputEl().dom.value = (v == '') ? '' :
39489                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
39490             
39491             if(!this.allowZero && v === '0') {
39492                 this.hiddenEl().dom.value = '';
39493                 this.inputEl().dom.value = '';
39494             }
39495             
39496             this.validate();
39497         }
39498     },
39499
39500     decimalPrecisionFcn : function(v)
39501     {
39502         return Math.floor(v);
39503     },
39504
39505     beforeBlur : function()
39506     {
39507         var v = this.parseValue(this.getRawValue());
39508         
39509         if(v || v === 0 || v === ''){
39510             this.setValue(v);
39511         }
39512     },
39513     
39514     hiddenEl : function()
39515     {
39516         return this.el.select('input.hidden-number-input',true).first();
39517     }
39518     
39519 });
39520
39521  
39522
39523 /*
39524 * Licence: LGPL
39525 */
39526
39527 /**
39528  * @class Roo.bootstrap.DocumentSlider
39529  * @extends Roo.bootstrap.Component
39530  * Bootstrap DocumentSlider class
39531  * 
39532  * @constructor
39533  * Create a new DocumentViewer
39534  * @param {Object} config The config object
39535  */
39536
39537 Roo.bootstrap.DocumentSlider = function(config){
39538     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
39539     
39540     this.files = [];
39541     
39542     this.addEvents({
39543         /**
39544          * @event initial
39545          * Fire after initEvent
39546          * @param {Roo.bootstrap.DocumentSlider} this
39547          */
39548         "initial" : true,
39549         /**
39550          * @event update
39551          * Fire after update
39552          * @param {Roo.bootstrap.DocumentSlider} this
39553          */
39554         "update" : true,
39555         /**
39556          * @event click
39557          * Fire after click
39558          * @param {Roo.bootstrap.DocumentSlider} this
39559          */
39560         "click" : true
39561     });
39562 };
39563
39564 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
39565     
39566     files : false,
39567     
39568     indicator : 0,
39569     
39570     getAutoCreate : function()
39571     {
39572         var cfg = {
39573             tag : 'div',
39574             cls : 'roo-document-slider',
39575             cn : [
39576                 {
39577                     tag : 'div',
39578                     cls : 'roo-document-slider-header',
39579                     cn : [
39580                         {
39581                             tag : 'div',
39582                             cls : 'roo-document-slider-header-title'
39583                         }
39584                     ]
39585                 },
39586                 {
39587                     tag : 'div',
39588                     cls : 'roo-document-slider-body',
39589                     cn : [
39590                         {
39591                             tag : 'div',
39592                             cls : 'roo-document-slider-prev',
39593                             cn : [
39594                                 {
39595                                     tag : 'i',
39596                                     cls : 'fa fa-chevron-left'
39597                                 }
39598                             ]
39599                         },
39600                         {
39601                             tag : 'div',
39602                             cls : 'roo-document-slider-thumb',
39603                             cn : [
39604                                 {
39605                                     tag : 'img',
39606                                     cls : 'roo-document-slider-image'
39607                                 }
39608                             ]
39609                         },
39610                         {
39611                             tag : 'div',
39612                             cls : 'roo-document-slider-next',
39613                             cn : [
39614                                 {
39615                                     tag : 'i',
39616                                     cls : 'fa fa-chevron-right'
39617                                 }
39618                             ]
39619                         }
39620                     ]
39621                 }
39622             ]
39623         };
39624         
39625         return cfg;
39626     },
39627     
39628     initEvents : function()
39629     {
39630         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
39631         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
39632         
39633         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
39634         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
39635         
39636         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
39637         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
39638         
39639         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
39640         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
39641         
39642         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
39643         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
39644         
39645         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
39646         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39647         
39648         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
39649         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39650         
39651         this.thumbEl.on('click', this.onClick, this);
39652         
39653         this.prevIndicator.on('click', this.prev, this);
39654         
39655         this.nextIndicator.on('click', this.next, this);
39656         
39657     },
39658     
39659     initial : function()
39660     {
39661         if(this.files.length){
39662             this.indicator = 1;
39663             this.update()
39664         }
39665         
39666         this.fireEvent('initial', this);
39667     },
39668     
39669     update : function()
39670     {
39671         this.imageEl.attr('src', this.files[this.indicator - 1]);
39672         
39673         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
39674         
39675         this.prevIndicator.show();
39676         
39677         if(this.indicator == 1){
39678             this.prevIndicator.hide();
39679         }
39680         
39681         this.nextIndicator.show();
39682         
39683         if(this.indicator == this.files.length){
39684             this.nextIndicator.hide();
39685         }
39686         
39687         this.thumbEl.scrollTo('top');
39688         
39689         this.fireEvent('update', this);
39690     },
39691     
39692     onClick : function(e)
39693     {
39694         e.preventDefault();
39695         
39696         this.fireEvent('click', this);
39697     },
39698     
39699     prev : function(e)
39700     {
39701         e.preventDefault();
39702         
39703         this.indicator = Math.max(1, this.indicator - 1);
39704         
39705         this.update();
39706     },
39707     
39708     next : function(e)
39709     {
39710         e.preventDefault();
39711         
39712         this.indicator = Math.min(this.files.length, this.indicator + 1);
39713         
39714         this.update();
39715     }
39716 });
39717 /*
39718  * - LGPL
39719  *
39720  * RadioSet
39721  *
39722  *
39723  */
39724
39725 /**
39726  * @class Roo.bootstrap.form.RadioSet
39727  * @extends Roo.bootstrap.form.Input
39728  * @children Roo.bootstrap.form.Radio
39729  * Bootstrap RadioSet class
39730  * @cfg {String} indicatorpos (left|right) default left
39731  * @cfg {Boolean} inline (true|false) inline the element (default true)
39732  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
39733  * @constructor
39734  * Create a new RadioSet
39735  * @param {Object} config The config object
39736  */
39737
39738 Roo.bootstrap.form.RadioSet = function(config){
39739     
39740     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
39741     
39742     this.radioes = [];
39743     
39744     Roo.bootstrap.form.RadioSet.register(this);
39745     
39746     this.addEvents({
39747         /**
39748         * @event check
39749         * Fires when the element is checked or unchecked.
39750         * @param {Roo.bootstrap.form.RadioSet} this This radio
39751         * @param {Roo.bootstrap.form.Radio} item The checked item
39752         */
39753        check : true,
39754        /**
39755         * @event click
39756         * Fires when the element is click.
39757         * @param {Roo.bootstrap.form.RadioSet} this This radio set
39758         * @param {Roo.bootstrap.form.Radio} item The checked item
39759         * @param {Roo.EventObject} e The event object
39760         */
39761        click : true
39762     });
39763     
39764 };
39765
39766 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
39767
39768     radioes : false,
39769     
39770     inline : true,
39771     
39772     weight : '',
39773     
39774     indicatorpos : 'left',
39775     
39776     getAutoCreate : function()
39777     {
39778         var label = {
39779             tag : 'label',
39780             cls : 'roo-radio-set-label',
39781             cn : [
39782                 {
39783                     tag : 'span',
39784                     html : this.fieldLabel
39785                 }
39786             ]
39787         };
39788         if (Roo.bootstrap.version == 3) {
39789             
39790             
39791             if(this.indicatorpos == 'left'){
39792                 label.cn.unshift({
39793                     tag : 'i',
39794                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
39795                     tooltip : 'This field is required'
39796                 });
39797             } else {
39798                 label.cn.push({
39799                     tag : 'i',
39800                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
39801                     tooltip : 'This field is required'
39802                 });
39803             }
39804         }
39805         var items = {
39806             tag : 'div',
39807             cls : 'roo-radio-set-items'
39808         };
39809         
39810         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
39811         
39812         if (align === 'left' && this.fieldLabel.length) {
39813             
39814             items = {
39815                 cls : "roo-radio-set-right", 
39816                 cn: [
39817                     items
39818                 ]
39819             };
39820             
39821             if(this.labelWidth > 12){
39822                 label.style = "width: " + this.labelWidth + 'px';
39823             }
39824             
39825             if(this.labelWidth < 13 && this.labelmd == 0){
39826                 this.labelmd = this.labelWidth;
39827             }
39828             
39829             if(this.labellg > 0){
39830                 label.cls += ' col-lg-' + this.labellg;
39831                 items.cls += ' col-lg-' + (12 - this.labellg);
39832             }
39833             
39834             if(this.labelmd > 0){
39835                 label.cls += ' col-md-' + this.labelmd;
39836                 items.cls += ' col-md-' + (12 - this.labelmd);
39837             }
39838             
39839             if(this.labelsm > 0){
39840                 label.cls += ' col-sm-' + this.labelsm;
39841                 items.cls += ' col-sm-' + (12 - this.labelsm);
39842             }
39843             
39844             if(this.labelxs > 0){
39845                 label.cls += ' col-xs-' + this.labelxs;
39846                 items.cls += ' col-xs-' + (12 - this.labelxs);
39847             }
39848         }
39849         
39850         var cfg = {
39851             tag : 'div',
39852             cls : 'roo-radio-set',
39853             cn : [
39854                 {
39855                     tag : 'input',
39856                     cls : 'roo-radio-set-input',
39857                     type : 'hidden',
39858                     name : this.name,
39859                     value : this.value ? this.value :  ''
39860                 },
39861                 label,
39862                 items
39863             ]
39864         };
39865         
39866         if(this.weight.length){
39867             cfg.cls += ' roo-radio-' + this.weight;
39868         }
39869         
39870         if(this.inline) {
39871             cfg.cls += ' roo-radio-set-inline';
39872         }
39873         
39874         var settings=this;
39875         ['xs','sm','md','lg'].map(function(size){
39876             if (settings[size]) {
39877                 cfg.cls += ' col-' + size + '-' + settings[size];
39878             }
39879         });
39880         
39881         return cfg;
39882         
39883     },
39884
39885     initEvents : function()
39886     {
39887         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
39888         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
39889         
39890         if(!this.fieldLabel.length){
39891             this.labelEl.hide();
39892         }
39893         
39894         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
39895         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
39896         
39897         this.indicator = this.indicatorEl();
39898         
39899         if(this.indicator){
39900             this.indicator.addClass('invisible');
39901         }
39902         
39903         this.originalValue = this.getValue();
39904         
39905     },
39906     
39907     inputEl: function ()
39908     {
39909         return this.el.select('.roo-radio-set-input', true).first();
39910     },
39911     
39912     getChildContainer : function()
39913     {
39914         return this.itemsEl;
39915     },
39916     
39917     register : function(item)
39918     {
39919         this.radioes.push(item);
39920         
39921     },
39922     
39923     validate : function()
39924     {   
39925         if(this.getVisibilityEl().hasClass('hidden')){
39926             return true;
39927         }
39928         
39929         var valid = false;
39930         
39931         Roo.each(this.radioes, function(i){
39932             if(!i.checked){
39933                 return;
39934             }
39935             
39936             valid = true;
39937             return false;
39938         });
39939         
39940         if(this.allowBlank) {
39941             return true;
39942         }
39943         
39944         if(this.disabled || valid){
39945             this.markValid();
39946             return true;
39947         }
39948         
39949         this.markInvalid();
39950         return false;
39951         
39952     },
39953     
39954     markValid : function()
39955     {
39956         if(this.labelEl.isVisible(true) && this.indicatorEl()){
39957             this.indicatorEl().removeClass('visible');
39958             this.indicatorEl().addClass('invisible');
39959         }
39960         
39961         
39962         if (Roo.bootstrap.version == 3) {
39963             this.el.removeClass([this.invalidClass, this.validClass]);
39964             this.el.addClass(this.validClass);
39965         } else {
39966             this.el.removeClass(['is-invalid','is-valid']);
39967             this.el.addClass(['is-valid']);
39968         }
39969         this.fireEvent('valid', this);
39970     },
39971     
39972     markInvalid : function(msg)
39973     {
39974         if(this.allowBlank || this.disabled){
39975             return;
39976         }
39977         
39978         if(this.labelEl.isVisible(true) && this.indicatorEl()){
39979             this.indicatorEl().removeClass('invisible');
39980             this.indicatorEl().addClass('visible');
39981         }
39982         if (Roo.bootstrap.version == 3) {
39983             this.el.removeClass([this.invalidClass, this.validClass]);
39984             this.el.addClass(this.invalidClass);
39985         } else {
39986             this.el.removeClass(['is-invalid','is-valid']);
39987             this.el.addClass(['is-invalid']);
39988         }
39989         
39990         this.fireEvent('invalid', this, msg);
39991         
39992     },
39993     
39994     setValue : function(v, suppressEvent)
39995     {   
39996         if(this.value === v){
39997             return;
39998         }
39999         
40000         this.value = v;
40001         
40002         if(this.rendered){
40003             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40004         }
40005         
40006         Roo.each(this.radioes, function(i){
40007             i.checked = false;
40008             i.el.removeClass('checked');
40009         });
40010         
40011         Roo.each(this.radioes, function(i){
40012             
40013             if(i.value === v || i.value.toString() === v.toString()){
40014                 i.checked = true;
40015                 i.el.addClass('checked');
40016                 
40017                 if(suppressEvent !== true){
40018                     this.fireEvent('check', this, i);
40019                 }
40020                 
40021                 return false;
40022             }
40023             
40024         }, this);
40025         
40026         this.validate();
40027     },
40028     
40029     clearInvalid : function(){
40030         
40031         if(!this.el || this.preventMark){
40032             return;
40033         }
40034         
40035         this.el.removeClass([this.invalidClass]);
40036         
40037         this.fireEvent('valid', this);
40038     }
40039     
40040 });
40041
40042 Roo.apply(Roo.bootstrap.form.RadioSet, {
40043     
40044     groups: {},
40045     
40046     register : function(set)
40047     {
40048         this.groups[set.name] = set;
40049     },
40050     
40051     get: function(name) 
40052     {
40053         if (typeof(this.groups[name]) == 'undefined') {
40054             return false;
40055         }
40056         
40057         return this.groups[name] ;
40058     }
40059     
40060 });
40061 /*
40062  * Based on:
40063  * Ext JS Library 1.1.1
40064  * Copyright(c) 2006-2007, Ext JS, LLC.
40065  *
40066  * Originally Released Under LGPL - original licence link has changed is not relivant.
40067  *
40068  * Fork - LGPL
40069  * <script type="text/javascript">
40070  */
40071
40072
40073 /**
40074  * @class Roo.bootstrap.SplitBar
40075  * @extends Roo.util.Observable
40076  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
40077  * <br><br>
40078  * Usage:
40079  * <pre><code>
40080 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
40081                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
40082 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
40083 split.minSize = 100;
40084 split.maxSize = 600;
40085 split.animate = true;
40086 split.on('moved', splitterMoved);
40087 </code></pre>
40088  * @constructor
40089  * Create a new SplitBar
40090  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
40091  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
40092  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40093  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
40094                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
40095                         position of the SplitBar).
40096  */
40097 Roo.bootstrap.SplitBar = function(cfg){
40098     
40099     /** @private */
40100     
40101     //{
40102     //  dragElement : elm
40103     //  resizingElement: el,
40104         // optional..
40105     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
40106     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
40107         // existingProxy ???
40108     //}
40109     
40110     this.el = Roo.get(cfg.dragElement, true);
40111     this.el.dom.unselectable = "on";
40112     /** @private */
40113     this.resizingEl = Roo.get(cfg.resizingElement, true);
40114
40115     /**
40116      * @private
40117      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40118      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
40119      * @type Number
40120      */
40121     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
40122     
40123     /**
40124      * The minimum size of the resizing element. (Defaults to 0)
40125      * @type Number
40126      */
40127     this.minSize = 0;
40128     
40129     /**
40130      * The maximum size of the resizing element. (Defaults to 2000)
40131      * @type Number
40132      */
40133     this.maxSize = 2000;
40134     
40135     /**
40136      * Whether to animate the transition to the new size
40137      * @type Boolean
40138      */
40139     this.animate = false;
40140     
40141     /**
40142      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
40143      * @type Boolean
40144      */
40145     this.useShim = false;
40146     
40147     /** @private */
40148     this.shim = null;
40149     
40150     if(!cfg.existingProxy){
40151         /** @private */
40152         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
40153     }else{
40154         this.proxy = Roo.get(cfg.existingProxy).dom;
40155     }
40156     /** @private */
40157     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
40158     
40159     /** @private */
40160     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
40161     
40162     /** @private */
40163     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
40164     
40165     /** @private */
40166     this.dragSpecs = {};
40167     
40168     /**
40169      * @private The adapter to use to positon and resize elements
40170      */
40171     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40172     this.adapter.init(this);
40173     
40174     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40175         /** @private */
40176         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
40177         this.el.addClass("roo-splitbar-h");
40178     }else{
40179         /** @private */
40180         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
40181         this.el.addClass("roo-splitbar-v");
40182     }
40183     
40184     this.addEvents({
40185         /**
40186          * @event resize
40187          * Fires when the splitter is moved (alias for {@link #event-moved})
40188          * @param {Roo.bootstrap.SplitBar} this
40189          * @param {Number} newSize the new width or height
40190          */
40191         "resize" : true,
40192         /**
40193          * @event moved
40194          * Fires when the splitter is moved
40195          * @param {Roo.bootstrap.SplitBar} this
40196          * @param {Number} newSize the new width or height
40197          */
40198         "moved" : true,
40199         /**
40200          * @event beforeresize
40201          * Fires before the splitter is dragged
40202          * @param {Roo.bootstrap.SplitBar} this
40203          */
40204         "beforeresize" : true,
40205
40206         "beforeapply" : true
40207     });
40208
40209     Roo.util.Observable.call(this);
40210 };
40211
40212 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
40213     onStartProxyDrag : function(x, y){
40214         this.fireEvent("beforeresize", this);
40215         if(!this.overlay){
40216             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
40217             o.unselectable();
40218             o.enableDisplayMode("block");
40219             // all splitbars share the same overlay
40220             Roo.bootstrap.SplitBar.prototype.overlay = o;
40221         }
40222         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
40223         this.overlay.show();
40224         Roo.get(this.proxy).setDisplayed("block");
40225         var size = this.adapter.getElementSize(this);
40226         this.activeMinSize = this.getMinimumSize();;
40227         this.activeMaxSize = this.getMaximumSize();;
40228         var c1 = size - this.activeMinSize;
40229         var c2 = Math.max(this.activeMaxSize - size, 0);
40230         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40231             this.dd.resetConstraints();
40232             this.dd.setXConstraint(
40233                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
40234                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
40235             );
40236             this.dd.setYConstraint(0, 0);
40237         }else{
40238             this.dd.resetConstraints();
40239             this.dd.setXConstraint(0, 0);
40240             this.dd.setYConstraint(
40241                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
40242                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
40243             );
40244          }
40245         this.dragSpecs.startSize = size;
40246         this.dragSpecs.startPoint = [x, y];
40247         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
40248     },
40249     
40250     /** 
40251      * @private Called after the drag operation by the DDProxy
40252      */
40253     onEndProxyDrag : function(e){
40254         Roo.get(this.proxy).setDisplayed(false);
40255         var endPoint = Roo.lib.Event.getXY(e);
40256         if(this.overlay){
40257             this.overlay.hide();
40258         }
40259         var newSize;
40260         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40261             newSize = this.dragSpecs.startSize + 
40262                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
40263                     endPoint[0] - this.dragSpecs.startPoint[0] :
40264                     this.dragSpecs.startPoint[0] - endPoint[0]
40265                 );
40266         }else{
40267             newSize = this.dragSpecs.startSize + 
40268                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
40269                     endPoint[1] - this.dragSpecs.startPoint[1] :
40270                     this.dragSpecs.startPoint[1] - endPoint[1]
40271                 );
40272         }
40273         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
40274         if(newSize != this.dragSpecs.startSize){
40275             if(this.fireEvent('beforeapply', this, newSize) !== false){
40276                 this.adapter.setElementSize(this, newSize);
40277                 this.fireEvent("moved", this, newSize);
40278                 this.fireEvent("resize", this, newSize);
40279             }
40280         }
40281     },
40282     
40283     /**
40284      * Get the adapter this SplitBar uses
40285      * @return The adapter object
40286      */
40287     getAdapter : function(){
40288         return this.adapter;
40289     },
40290     
40291     /**
40292      * Set the adapter this SplitBar uses
40293      * @param {Object} adapter A SplitBar adapter object
40294      */
40295     setAdapter : function(adapter){
40296         this.adapter = adapter;
40297         this.adapter.init(this);
40298     },
40299     
40300     /**
40301      * Gets the minimum size for the resizing element
40302      * @return {Number} The minimum size
40303      */
40304     getMinimumSize : function(){
40305         return this.minSize;
40306     },
40307     
40308     /**
40309      * Sets the minimum size for the resizing element
40310      * @param {Number} minSize The minimum size
40311      */
40312     setMinimumSize : function(minSize){
40313         this.minSize = minSize;
40314     },
40315     
40316     /**
40317      * Gets the maximum size for the resizing element
40318      * @return {Number} The maximum size
40319      */
40320     getMaximumSize : function(){
40321         return this.maxSize;
40322     },
40323     
40324     /**
40325      * Sets the maximum size for the resizing element
40326      * @param {Number} maxSize The maximum size
40327      */
40328     setMaximumSize : function(maxSize){
40329         this.maxSize = maxSize;
40330     },
40331     
40332     /**
40333      * Sets the initialize size for the resizing element
40334      * @param {Number} size The initial size
40335      */
40336     setCurrentSize : function(size){
40337         var oldAnimate = this.animate;
40338         this.animate = false;
40339         this.adapter.setElementSize(this, size);
40340         this.animate = oldAnimate;
40341     },
40342     
40343     /**
40344      * Destroy this splitbar. 
40345      * @param {Boolean} removeEl True to remove the element
40346      */
40347     destroy : function(removeEl){
40348         if(this.shim){
40349             this.shim.remove();
40350         }
40351         this.dd.unreg();
40352         this.proxy.parentNode.removeChild(this.proxy);
40353         if(removeEl){
40354             this.el.remove();
40355         }
40356     }
40357 });
40358
40359 /**
40360  * @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.
40361  */
40362 Roo.bootstrap.SplitBar.createProxy = function(dir){
40363     var proxy = new Roo.Element(document.createElement("div"));
40364     proxy.unselectable();
40365     var cls = 'roo-splitbar-proxy';
40366     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
40367     document.body.appendChild(proxy.dom);
40368     return proxy.dom;
40369 };
40370
40371 /** 
40372  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
40373  * Default Adapter. It assumes the splitter and resizing element are not positioned
40374  * elements and only gets/sets the width of the element. Generally used for table based layouts.
40375  */
40376 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
40377 };
40378
40379 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
40380     // do nothing for now
40381     init : function(s){
40382     
40383     },
40384     /**
40385      * Called before drag operations to get the current size of the resizing element. 
40386      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40387      */
40388      getElementSize : function(s){
40389         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40390             return s.resizingEl.getWidth();
40391         }else{
40392             return s.resizingEl.getHeight();
40393         }
40394     },
40395     
40396     /**
40397      * Called after drag operations to set the size of the resizing element.
40398      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40399      * @param {Number} newSize The new size to set
40400      * @param {Function} onComplete A function to be invoked when resizing is complete
40401      */
40402     setElementSize : function(s, newSize, onComplete){
40403         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40404             if(!s.animate){
40405                 s.resizingEl.setWidth(newSize);
40406                 if(onComplete){
40407                     onComplete(s, newSize);
40408                 }
40409             }else{
40410                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
40411             }
40412         }else{
40413             
40414             if(!s.animate){
40415                 s.resizingEl.setHeight(newSize);
40416                 if(onComplete){
40417                     onComplete(s, newSize);
40418                 }
40419             }else{
40420                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
40421             }
40422         }
40423     }
40424 };
40425
40426 /** 
40427  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
40428  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
40429  * Adapter that  moves the splitter element to align with the resized sizing element. 
40430  * Used with an absolute positioned SplitBar.
40431  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
40432  * document.body, make sure you assign an id to the body element.
40433  */
40434 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
40435     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40436     this.container = Roo.get(container);
40437 };
40438
40439 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
40440     init : function(s){
40441         this.basic.init(s);
40442     },
40443     
40444     getElementSize : function(s){
40445         return this.basic.getElementSize(s);
40446     },
40447     
40448     setElementSize : function(s, newSize, onComplete){
40449         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
40450     },
40451     
40452     moveSplitter : function(s){
40453         var yes = Roo.bootstrap.SplitBar;
40454         switch(s.placement){
40455             case yes.LEFT:
40456                 s.el.setX(s.resizingEl.getRight());
40457                 break;
40458             case yes.RIGHT:
40459                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
40460                 break;
40461             case yes.TOP:
40462                 s.el.setY(s.resizingEl.getBottom());
40463                 break;
40464             case yes.BOTTOM:
40465                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
40466                 break;
40467         }
40468     }
40469 };
40470
40471 /**
40472  * Orientation constant - Create a vertical SplitBar
40473  * @static
40474  * @type Number
40475  */
40476 Roo.bootstrap.SplitBar.VERTICAL = 1;
40477
40478 /**
40479  * Orientation constant - Create a horizontal SplitBar
40480  * @static
40481  * @type Number
40482  */
40483 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
40484
40485 /**
40486  * Placement constant - The resizing element is to the left of the splitter element
40487  * @static
40488  * @type Number
40489  */
40490 Roo.bootstrap.SplitBar.LEFT = 1;
40491
40492 /**
40493  * Placement constant - The resizing element is to the right of the splitter element
40494  * @static
40495  * @type Number
40496  */
40497 Roo.bootstrap.SplitBar.RIGHT = 2;
40498
40499 /**
40500  * Placement constant - The resizing element is positioned above the splitter element
40501  * @static
40502  * @type Number
40503  */
40504 Roo.bootstrap.SplitBar.TOP = 3;
40505
40506 /**
40507  * Placement constant - The resizing element is positioned under splitter element
40508  * @static
40509  * @type Number
40510  */
40511 Roo.bootstrap.SplitBar.BOTTOM = 4;
40512 /*
40513  * Based on:
40514  * Ext JS Library 1.1.1
40515  * Copyright(c) 2006-2007, Ext JS, LLC.
40516  *
40517  * Originally Released Under LGPL - original licence link has changed is not relivant.
40518  *
40519  * Fork - LGPL
40520  * <script type="text/javascript">
40521  */
40522
40523 /**
40524  * @class Roo.bootstrap.layout.Manager
40525  * @extends Roo.bootstrap.Component
40526  * @abstract
40527  * Base class for layout managers.
40528  */
40529 Roo.bootstrap.layout.Manager = function(config)
40530 {
40531     this.monitorWindowResize = true; // do this before we apply configuration.
40532     
40533     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
40534
40535
40536
40537
40538
40539     /** false to disable window resize monitoring @type Boolean */
40540     
40541     this.regions = {};
40542     this.addEvents({
40543         /**
40544          * @event layout
40545          * Fires when a layout is performed.
40546          * @param {Roo.LayoutManager} this
40547          */
40548         "layout" : true,
40549         /**
40550          * @event regionresized
40551          * Fires when the user resizes a region.
40552          * @param {Roo.LayoutRegion} region The resized region
40553          * @param {Number} newSize The new size (width for east/west, height for north/south)
40554          */
40555         "regionresized" : true,
40556         /**
40557          * @event regioncollapsed
40558          * Fires when a region is collapsed.
40559          * @param {Roo.LayoutRegion} region The collapsed region
40560          */
40561         "regioncollapsed" : true,
40562         /**
40563          * @event regionexpanded
40564          * Fires when a region is expanded.
40565          * @param {Roo.LayoutRegion} region The expanded region
40566          */
40567         "regionexpanded" : true
40568     });
40569     this.updating = false;
40570
40571     if (config.el) {
40572         this.el = Roo.get(config.el);
40573         this.initEvents();
40574     }
40575
40576 };
40577
40578 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
40579
40580
40581     regions : null,
40582
40583     monitorWindowResize : true,
40584
40585
40586     updating : false,
40587
40588
40589     onRender : function(ct, position)
40590     {
40591         if(!this.el){
40592             this.el = Roo.get(ct);
40593             this.initEvents();
40594         }
40595         //this.fireEvent('render',this);
40596     },
40597
40598
40599     initEvents: function()
40600     {
40601
40602
40603         // ie scrollbar fix
40604         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
40605             document.body.scroll = "no";
40606         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
40607             this.el.position('relative');
40608         }
40609         this.id = this.el.id;
40610         this.el.addClass("roo-layout-container");
40611         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
40612         if(this.el.dom != document.body ) {
40613             this.el.on('resize', this.layout,this);
40614             this.el.on('show', this.layout,this);
40615         }
40616
40617     },
40618
40619     /**
40620      * Returns true if this layout is currently being updated
40621      * @return {Boolean}
40622      */
40623     isUpdating : function(){
40624         return this.updating;
40625     },
40626
40627     /**
40628      * Suspend the LayoutManager from doing auto-layouts while
40629      * making multiple add or remove calls
40630      */
40631     beginUpdate : function(){
40632         this.updating = true;
40633     },
40634
40635     /**
40636      * Restore auto-layouts and optionally disable the manager from performing a layout
40637      * @param {Boolean} noLayout true to disable a layout update
40638      */
40639     endUpdate : function(noLayout){
40640         this.updating = false;
40641         if(!noLayout){
40642             this.layout();
40643         }
40644     },
40645
40646     layout: function(){
40647         // abstract...
40648     },
40649
40650     onRegionResized : function(region, newSize){
40651         this.fireEvent("regionresized", region, newSize);
40652         this.layout();
40653     },
40654
40655     onRegionCollapsed : function(region){
40656         this.fireEvent("regioncollapsed", region);
40657     },
40658
40659     onRegionExpanded : function(region){
40660         this.fireEvent("regionexpanded", region);
40661     },
40662
40663     /**
40664      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
40665      * performs box-model adjustments.
40666      * @return {Object} The size as an object {width: (the width), height: (the height)}
40667      */
40668     getViewSize : function()
40669     {
40670         var size;
40671         if(this.el.dom != document.body){
40672             size = this.el.getSize();
40673         }else{
40674             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
40675         }
40676         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
40677         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40678         return size;
40679     },
40680
40681     /**
40682      * Returns the Element this layout is bound to.
40683      * @return {Roo.Element}
40684      */
40685     getEl : function(){
40686         return this.el;
40687     },
40688
40689     /**
40690      * Returns the specified region.
40691      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
40692      * @return {Roo.LayoutRegion}
40693      */
40694     getRegion : function(target){
40695         return this.regions[target.toLowerCase()];
40696     },
40697
40698     onWindowResize : function(){
40699         if(this.monitorWindowResize){
40700             this.layout();
40701         }
40702     }
40703 });
40704 /*
40705  * Based on:
40706  * Ext JS Library 1.1.1
40707  * Copyright(c) 2006-2007, Ext JS, LLC.
40708  *
40709  * Originally Released Under LGPL - original licence link has changed is not relivant.
40710  *
40711  * Fork - LGPL
40712  * <script type="text/javascript">
40713  */
40714 /**
40715  * @class Roo.bootstrap.layout.Border
40716  * @extends Roo.bootstrap.layout.Manager
40717  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
40718  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
40719  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
40720  * please see: examples/bootstrap/nested.html<br><br>
40721  
40722 <b>The container the layout is rendered into can be either the body element or any other element.
40723 If it is not the body element, the container needs to either be an absolute positioned element,
40724 or you will need to add "position:relative" to the css of the container.  You will also need to specify
40725 the container size if it is not the body element.</b>
40726
40727 * @constructor
40728 * Create a new Border
40729 * @param {Object} config Configuration options
40730  */
40731 Roo.bootstrap.layout.Border = function(config){
40732     config = config || {};
40733     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
40734     
40735     
40736     
40737     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
40738         if(config[region]){
40739             config[region].region = region;
40740             this.addRegion(config[region]);
40741         }
40742     },this);
40743     
40744 };
40745
40746 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
40747
40748 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
40749     
40750         /**
40751          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
40752          */
40753         /**
40754          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
40755          */
40756         /**
40757          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
40758          */
40759         /**
40760          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
40761          */
40762         /**
40763          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
40764          */
40765         
40766         
40767         
40768         
40769     parent : false, // this might point to a 'nest' or a ???
40770     
40771     /**
40772      * Creates and adds a new region if it doesn't already exist.
40773      * @param {String} target The target region key (north, south, east, west or center).
40774      * @param {Object} config The regions config object
40775      * @return {BorderLayoutRegion} The new region
40776      */
40777     addRegion : function(config)
40778     {
40779         if(!this.regions[config.region]){
40780             var r = this.factory(config);
40781             this.bindRegion(r);
40782         }
40783         return this.regions[config.region];
40784     },
40785
40786     // private (kinda)
40787     bindRegion : function(r){
40788         this.regions[r.config.region] = r;
40789         
40790         r.on("visibilitychange",    this.layout, this);
40791         r.on("paneladded",          this.layout, this);
40792         r.on("panelremoved",        this.layout, this);
40793         r.on("invalidated",         this.layout, this);
40794         r.on("resized",             this.onRegionResized, this);
40795         r.on("collapsed",           this.onRegionCollapsed, this);
40796         r.on("expanded",            this.onRegionExpanded, this);
40797     },
40798
40799     /**
40800      * Performs a layout update.
40801      */
40802     layout : function()
40803     {
40804         if(this.updating) {
40805             return;
40806         }
40807         
40808         // render all the rebions if they have not been done alreayd?
40809         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
40810             if(this.regions[region] && !this.regions[region].bodyEl){
40811                 this.regions[region].onRender(this.el)
40812             }
40813         },this);
40814         
40815         var size = this.getViewSize();
40816         var w = size.width;
40817         var h = size.height;
40818         var centerW = w;
40819         var centerH = h;
40820         var centerY = 0;
40821         var centerX = 0;
40822         //var x = 0, y = 0;
40823
40824         var rs = this.regions;
40825         var north = rs["north"];
40826         var south = rs["south"]; 
40827         var west = rs["west"];
40828         var east = rs["east"];
40829         var center = rs["center"];
40830         //if(this.hideOnLayout){ // not supported anymore
40831             //c.el.setStyle("display", "none");
40832         //}
40833         if(north && north.isVisible()){
40834             var b = north.getBox();
40835             var m = north.getMargins();
40836             b.width = w - (m.left+m.right);
40837             b.x = m.left;
40838             b.y = m.top;
40839             centerY = b.height + b.y + m.bottom;
40840             centerH -= centerY;
40841             north.updateBox(this.safeBox(b));
40842         }
40843         if(south && south.isVisible()){
40844             var b = south.getBox();
40845             var m = south.getMargins();
40846             b.width = w - (m.left+m.right);
40847             b.x = m.left;
40848             var totalHeight = (b.height + m.top + m.bottom);
40849             b.y = h - totalHeight + m.top;
40850             centerH -= totalHeight;
40851             south.updateBox(this.safeBox(b));
40852         }
40853         if(west && west.isVisible()){
40854             var b = west.getBox();
40855             var m = west.getMargins();
40856             b.height = centerH - (m.top+m.bottom);
40857             b.x = m.left;
40858             b.y = centerY + m.top;
40859             var totalWidth = (b.width + m.left + m.right);
40860             centerX += totalWidth;
40861             centerW -= totalWidth;
40862             west.updateBox(this.safeBox(b));
40863         }
40864         if(east && east.isVisible()){
40865             var b = east.getBox();
40866             var m = east.getMargins();
40867             b.height = centerH - (m.top+m.bottom);
40868             var totalWidth = (b.width + m.left + m.right);
40869             b.x = w - totalWidth + m.left;
40870             b.y = centerY + m.top;
40871             centerW -= totalWidth;
40872             east.updateBox(this.safeBox(b));
40873         }
40874         if(center){
40875             var m = center.getMargins();
40876             var centerBox = {
40877                 x: centerX + m.left,
40878                 y: centerY + m.top,
40879                 width: centerW - (m.left+m.right),
40880                 height: centerH - (m.top+m.bottom)
40881             };
40882             //if(this.hideOnLayout){
40883                 //center.el.setStyle("display", "block");
40884             //}
40885             center.updateBox(this.safeBox(centerBox));
40886         }
40887         this.el.repaint();
40888         this.fireEvent("layout", this);
40889     },
40890
40891     // private
40892     safeBox : function(box){
40893         box.width = Math.max(0, box.width);
40894         box.height = Math.max(0, box.height);
40895         return box;
40896     },
40897
40898     /**
40899      * Adds a ContentPanel (or subclass) to this layout.
40900      * @param {String} target The target region key (north, south, east, west or center).
40901      * @param {Roo.ContentPanel} panel The panel to add
40902      * @return {Roo.ContentPanel} The added panel
40903      */
40904     add : function(target, panel){
40905          
40906         target = target.toLowerCase();
40907         return this.regions[target].add(panel);
40908     },
40909
40910     /**
40911      * Remove a ContentPanel (or subclass) to this layout.
40912      * @param {String} target The target region key (north, south, east, west or center).
40913      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
40914      * @return {Roo.ContentPanel} The removed panel
40915      */
40916     remove : function(target, panel){
40917         target = target.toLowerCase();
40918         return this.regions[target].remove(panel);
40919     },
40920
40921     /**
40922      * Searches all regions for a panel with the specified id
40923      * @param {String} panelId
40924      * @return {Roo.ContentPanel} The panel or null if it wasn't found
40925      */
40926     findPanel : function(panelId){
40927         var rs = this.regions;
40928         for(var target in rs){
40929             if(typeof rs[target] != "function"){
40930                 var p = rs[target].getPanel(panelId);
40931                 if(p){
40932                     return p;
40933                 }
40934             }
40935         }
40936         return null;
40937     },
40938
40939     /**
40940      * Searches all regions for a panel with the specified id and activates (shows) it.
40941      * @param {String/ContentPanel} panelId The panels id or the panel itself
40942      * @return {Roo.ContentPanel} The shown panel or null
40943      */
40944     showPanel : function(panelId) {
40945       var rs = this.regions;
40946       for(var target in rs){
40947          var r = rs[target];
40948          if(typeof r != "function"){
40949             if(r.hasPanel(panelId)){
40950                return r.showPanel(panelId);
40951             }
40952          }
40953       }
40954       return null;
40955    },
40956
40957    /**
40958      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
40959      * @param {Roo.state.Provider} provider (optional) An alternate state provider
40960      */
40961    /*
40962     restoreState : function(provider){
40963         if(!provider){
40964             provider = Roo.state.Manager;
40965         }
40966         var sm = new Roo.LayoutStateManager();
40967         sm.init(this, provider);
40968     },
40969 */
40970  
40971  
40972     /**
40973      * Adds a xtype elements to the layout.
40974      * <pre><code>
40975
40976 layout.addxtype({
40977        xtype : 'ContentPanel',
40978        region: 'west',
40979        items: [ .... ]
40980    }
40981 );
40982
40983 layout.addxtype({
40984         xtype : 'NestedLayoutPanel',
40985         region: 'west',
40986         layout: {
40987            center: { },
40988            west: { }   
40989         },
40990         items : [ ... list of content panels or nested layout panels.. ]
40991    }
40992 );
40993 </code></pre>
40994      * @param {Object} cfg Xtype definition of item to add.
40995      */
40996     addxtype : function(cfg)
40997     {
40998         // basically accepts a pannel...
40999         // can accept a layout region..!?!?
41000         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
41001         
41002         
41003         // theory?  children can only be panels??
41004         
41005         //if (!cfg.xtype.match(/Panel$/)) {
41006         //    return false;
41007         //}
41008         var ret = false;
41009         
41010         if (typeof(cfg.region) == 'undefined') {
41011             Roo.log("Failed to add Panel, region was not set");
41012             Roo.log(cfg);
41013             return false;
41014         }
41015         var region = cfg.region;
41016         delete cfg.region;
41017         
41018           
41019         var xitems = [];
41020         if (cfg.items) {
41021             xitems = cfg.items;
41022             delete cfg.items;
41023         }
41024         var nb = false;
41025         
41026         if ( region == 'center') {
41027             Roo.log("Center: " + cfg.title);
41028         }
41029         
41030         
41031         switch(cfg.xtype) 
41032         {
41033             case 'Content':  // ContentPanel (el, cfg)
41034             case 'Scroll':  // ContentPanel (el, cfg)
41035             case 'View': 
41036                 cfg.autoCreate = cfg.autoCreate || true;
41037                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41038                 //} else {
41039                 //    var el = this.el.createChild();
41040                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
41041                 //}
41042                 
41043                 this.add(region, ret);
41044                 break;
41045             
41046             /*
41047             case 'TreePanel': // our new panel!
41048                 cfg.el = this.el.createChild();
41049                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41050                 this.add(region, ret);
41051                 break;
41052             */
41053             
41054             case 'Nest': 
41055                 // create a new Layout (which is  a Border Layout...
41056                 
41057                 var clayout = cfg.layout;
41058                 clayout.el  = this.el.createChild();
41059                 clayout.items   = clayout.items  || [];
41060                 
41061                 delete cfg.layout;
41062                 
41063                 // replace this exitems with the clayout ones..
41064                 xitems = clayout.items;
41065                  
41066                 // force background off if it's in center...
41067                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
41068                     cfg.background = false;
41069                 }
41070                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
41071                 
41072                 
41073                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41074                 //console.log('adding nested layout panel '  + cfg.toSource());
41075                 this.add(region, ret);
41076                 nb = {}; /// find first...
41077                 break;
41078             
41079             case 'Grid':
41080                 
41081                 // needs grid and region
41082                 
41083                 //var el = this.getRegion(region).el.createChild();
41084                 /*
41085                  *var el = this.el.createChild();
41086                 // create the grid first...
41087                 cfg.grid.container = el;
41088                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
41089                 */
41090                 
41091                 if (region == 'center' && this.active ) {
41092                     cfg.background = false;
41093                 }
41094                 
41095                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41096                 
41097                 this.add(region, ret);
41098                 /*
41099                 if (cfg.background) {
41100                     // render grid on panel activation (if panel background)
41101                     ret.on('activate', function(gp) {
41102                         if (!gp.grid.rendered) {
41103                     //        gp.grid.render(el);
41104                         }
41105                     });
41106                 } else {
41107                   //  cfg.grid.render(el);
41108                 }
41109                 */
41110                 break;
41111            
41112            
41113             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
41114                 // it was the old xcomponent building that caused this before.
41115                 // espeically if border is the top element in the tree.
41116                 ret = this;
41117                 break; 
41118                 
41119                     
41120                 
41121                 
41122                 
41123             default:
41124                 /*
41125                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
41126                     
41127                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41128                     this.add(region, ret);
41129                 } else {
41130                 */
41131                     Roo.log(cfg);
41132                     throw "Can not add '" + cfg.xtype + "' to Border";
41133                     return null;
41134              
41135                                 
41136              
41137         }
41138         this.beginUpdate();
41139         // add children..
41140         var region = '';
41141         var abn = {};
41142         Roo.each(xitems, function(i)  {
41143             region = nb && i.region ? i.region : false;
41144             
41145             var add = ret.addxtype(i);
41146            
41147             if (region) {
41148                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
41149                 if (!i.background) {
41150                     abn[region] = nb[region] ;
41151                 }
41152             }
41153             
41154         });
41155         this.endUpdate();
41156
41157         // make the last non-background panel active..
41158         //if (nb) { Roo.log(abn); }
41159         if (nb) {
41160             
41161             for(var r in abn) {
41162                 region = this.getRegion(r);
41163                 if (region) {
41164                     // tried using nb[r], but it does not work..
41165                      
41166                     region.showPanel(abn[r]);
41167                    
41168                 }
41169             }
41170         }
41171         return ret;
41172         
41173     },
41174     
41175     
41176 // private
41177     factory : function(cfg)
41178     {
41179         
41180         var validRegions = Roo.bootstrap.layout.Border.regions;
41181
41182         var target = cfg.region;
41183         cfg.mgr = this;
41184         
41185         var r = Roo.bootstrap.layout;
41186         Roo.log(target);
41187         switch(target){
41188             case "north":
41189                 return new r.North(cfg);
41190             case "south":
41191                 return new r.South(cfg);
41192             case "east":
41193                 return new r.East(cfg);
41194             case "west":
41195                 return new r.West(cfg);
41196             case "center":
41197                 return new r.Center(cfg);
41198         }
41199         throw 'Layout region "'+target+'" not supported.';
41200     }
41201     
41202     
41203 });
41204  /*
41205  * Based on:
41206  * Ext JS Library 1.1.1
41207  * Copyright(c) 2006-2007, Ext JS, LLC.
41208  *
41209  * Originally Released Under LGPL - original licence link has changed is not relivant.
41210  *
41211  * Fork - LGPL
41212  * <script type="text/javascript">
41213  */
41214  
41215 /**
41216  * @class Roo.bootstrap.layout.Basic
41217  * @extends Roo.util.Observable
41218  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
41219  * and does not have a titlebar, tabs or any other features. All it does is size and position 
41220  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
41221  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
41222  * @cfg {string}   region  the region that it inhabits..
41223  * @cfg {bool}   skipConfig skip config?
41224  * 
41225
41226  */
41227 Roo.bootstrap.layout.Basic = function(config){
41228     
41229     this.mgr = config.mgr;
41230     
41231     this.position = config.region;
41232     
41233     var skipConfig = config.skipConfig;
41234     
41235     this.events = {
41236         /**
41237          * @scope Roo.BasicLayoutRegion
41238          */
41239         
41240         /**
41241          * @event beforeremove
41242          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
41243          * @param {Roo.LayoutRegion} this
41244          * @param {Roo.ContentPanel} panel The panel
41245          * @param {Object} e The cancel event object
41246          */
41247         "beforeremove" : true,
41248         /**
41249          * @event invalidated
41250          * Fires when the layout for this region is changed.
41251          * @param {Roo.LayoutRegion} this
41252          */
41253         "invalidated" : true,
41254         /**
41255          * @event visibilitychange
41256          * Fires when this region is shown or hidden 
41257          * @param {Roo.LayoutRegion} this
41258          * @param {Boolean} visibility true or false
41259          */
41260         "visibilitychange" : true,
41261         /**
41262          * @event paneladded
41263          * Fires when a panel is added. 
41264          * @param {Roo.LayoutRegion} this
41265          * @param {Roo.ContentPanel} panel The panel
41266          */
41267         "paneladded" : true,
41268         /**
41269          * @event panelremoved
41270          * Fires when a panel is removed. 
41271          * @param {Roo.LayoutRegion} this
41272          * @param {Roo.ContentPanel} panel The panel
41273          */
41274         "panelremoved" : true,
41275         /**
41276          * @event beforecollapse
41277          * Fires when this region before collapse.
41278          * @param {Roo.LayoutRegion} this
41279          */
41280         "beforecollapse" : true,
41281         /**
41282          * @event collapsed
41283          * Fires when this region is collapsed.
41284          * @param {Roo.LayoutRegion} this
41285          */
41286         "collapsed" : true,
41287         /**
41288          * @event expanded
41289          * Fires when this region is expanded.
41290          * @param {Roo.LayoutRegion} this
41291          */
41292         "expanded" : true,
41293         /**
41294          * @event slideshow
41295          * Fires when this region is slid into view.
41296          * @param {Roo.LayoutRegion} this
41297          */
41298         "slideshow" : true,
41299         /**
41300          * @event slidehide
41301          * Fires when this region slides out of view. 
41302          * @param {Roo.LayoutRegion} this
41303          */
41304         "slidehide" : true,
41305         /**
41306          * @event panelactivated
41307          * Fires when a panel is activated. 
41308          * @param {Roo.LayoutRegion} this
41309          * @param {Roo.ContentPanel} panel The activated panel
41310          */
41311         "panelactivated" : true,
41312         /**
41313          * @event resized
41314          * Fires when the user resizes this region. 
41315          * @param {Roo.LayoutRegion} this
41316          * @param {Number} newSize The new size (width for east/west, height for north/south)
41317          */
41318         "resized" : true
41319     };
41320     /** A collection of panels in this region. @type Roo.util.MixedCollection */
41321     this.panels = new Roo.util.MixedCollection();
41322     this.panels.getKey = this.getPanelId.createDelegate(this);
41323     this.box = null;
41324     this.activePanel = null;
41325     // ensure listeners are added...
41326     
41327     if (config.listeners || config.events) {
41328         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
41329             listeners : config.listeners || {},
41330             events : config.events || {}
41331         });
41332     }
41333     
41334     if(skipConfig !== true){
41335         this.applyConfig(config);
41336     }
41337 };
41338
41339 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
41340 {
41341     getPanelId : function(p){
41342         return p.getId();
41343     },
41344     
41345     applyConfig : function(config){
41346         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41347         this.config = config;
41348         
41349     },
41350     
41351     /**
41352      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
41353      * the width, for horizontal (north, south) the height.
41354      * @param {Number} newSize The new width or height
41355      */
41356     resizeTo : function(newSize){
41357         var el = this.el ? this.el :
41358                  (this.activePanel ? this.activePanel.getEl() : null);
41359         if(el){
41360             switch(this.position){
41361                 case "east":
41362                 case "west":
41363                     el.setWidth(newSize);
41364                     this.fireEvent("resized", this, newSize);
41365                 break;
41366                 case "north":
41367                 case "south":
41368                     el.setHeight(newSize);
41369                     this.fireEvent("resized", this, newSize);
41370                 break;                
41371             }
41372         }
41373     },
41374     
41375     getBox : function(){
41376         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
41377     },
41378     
41379     getMargins : function(){
41380         return this.margins;
41381     },
41382     
41383     updateBox : function(box){
41384         this.box = box;
41385         var el = this.activePanel.getEl();
41386         el.dom.style.left = box.x + "px";
41387         el.dom.style.top = box.y + "px";
41388         this.activePanel.setSize(box.width, box.height);
41389     },
41390     
41391     /**
41392      * Returns the container element for this region.
41393      * @return {Roo.Element}
41394      */
41395     getEl : function(){
41396         return this.activePanel;
41397     },
41398     
41399     /**
41400      * Returns true if this region is currently visible.
41401      * @return {Boolean}
41402      */
41403     isVisible : function(){
41404         return this.activePanel ? true : false;
41405     },
41406     
41407     setActivePanel : function(panel){
41408         panel = this.getPanel(panel);
41409         if(this.activePanel && this.activePanel != panel){
41410             this.activePanel.setActiveState(false);
41411             this.activePanel.getEl().setLeftTop(-10000,-10000);
41412         }
41413         this.activePanel = panel;
41414         panel.setActiveState(true);
41415         if(this.box){
41416             panel.setSize(this.box.width, this.box.height);
41417         }
41418         this.fireEvent("panelactivated", this, panel);
41419         this.fireEvent("invalidated");
41420     },
41421     
41422     /**
41423      * Show the specified panel.
41424      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
41425      * @return {Roo.ContentPanel} The shown panel or null
41426      */
41427     showPanel : function(panel){
41428         panel = this.getPanel(panel);
41429         if(panel){
41430             this.setActivePanel(panel);
41431         }
41432         return panel;
41433     },
41434     
41435     /**
41436      * Get the active panel for this region.
41437      * @return {Roo.ContentPanel} The active panel or null
41438      */
41439     getActivePanel : function(){
41440         return this.activePanel;
41441     },
41442     
41443     /**
41444      * Add the passed ContentPanel(s)
41445      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
41446      * @return {Roo.ContentPanel} The panel added (if only one was added)
41447      */
41448     add : function(panel){
41449         if(arguments.length > 1){
41450             for(var i = 0, len = arguments.length; i < len; i++) {
41451                 this.add(arguments[i]);
41452             }
41453             return null;
41454         }
41455         if(this.hasPanel(panel)){
41456             this.showPanel(panel);
41457             return panel;
41458         }
41459         var el = panel.getEl();
41460         if(el.dom.parentNode != this.mgr.el.dom){
41461             this.mgr.el.dom.appendChild(el.dom);
41462         }
41463         if(panel.setRegion){
41464             panel.setRegion(this);
41465         }
41466         this.panels.add(panel);
41467         el.setStyle("position", "absolute");
41468         if(!panel.background){
41469             this.setActivePanel(panel);
41470             if(this.config.initialSize && this.panels.getCount()==1){
41471                 this.resizeTo(this.config.initialSize);
41472             }
41473         }
41474         this.fireEvent("paneladded", this, panel);
41475         return panel;
41476     },
41477     
41478     /**
41479      * Returns true if the panel is in this region.
41480      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41481      * @return {Boolean}
41482      */
41483     hasPanel : function(panel){
41484         if(typeof panel == "object"){ // must be panel obj
41485             panel = panel.getId();
41486         }
41487         return this.getPanel(panel) ? true : false;
41488     },
41489     
41490     /**
41491      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
41492      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41493      * @param {Boolean} preservePanel Overrides the config preservePanel option
41494      * @return {Roo.ContentPanel} The panel that was removed
41495      */
41496     remove : function(panel, preservePanel){
41497         panel = this.getPanel(panel);
41498         if(!panel){
41499             return null;
41500         }
41501         var e = {};
41502         this.fireEvent("beforeremove", this, panel, e);
41503         if(e.cancel === true){
41504             return null;
41505         }
41506         var panelId = panel.getId();
41507         this.panels.removeKey(panelId);
41508         return panel;
41509     },
41510     
41511     /**
41512      * Returns the panel specified or null if it's not in this region.
41513      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41514      * @return {Roo.ContentPanel}
41515      */
41516     getPanel : function(id){
41517         if(typeof id == "object"){ // must be panel obj
41518             return id;
41519         }
41520         return this.panels.get(id);
41521     },
41522     
41523     /**
41524      * Returns this regions position (north/south/east/west/center).
41525      * @return {String} 
41526      */
41527     getPosition: function(){
41528         return this.position;    
41529     }
41530 });/*
41531  * Based on:
41532  * Ext JS Library 1.1.1
41533  * Copyright(c) 2006-2007, Ext JS, LLC.
41534  *
41535  * Originally Released Under LGPL - original licence link has changed is not relivant.
41536  *
41537  * Fork - LGPL
41538  * <script type="text/javascript">
41539  */
41540  
41541 /**
41542  * @class Roo.bootstrap.layout.Region
41543  * @extends Roo.bootstrap.layout.Basic
41544  * This class represents a region in a layout manager.
41545  
41546  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
41547  * @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})
41548  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
41549  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
41550  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
41551  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
41552  * @cfg {String}    title           The title for the region (overrides panel titles)
41553  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
41554  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
41555  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
41556  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
41557  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
41558  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
41559  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
41560  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
41561  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
41562  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
41563
41564  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
41565  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
41566  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
41567  * @cfg {Number}    width           For East/West panels
41568  * @cfg {Number}    height          For North/South panels
41569  * @cfg {Boolean}   split           To show the splitter
41570  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
41571  * 
41572  * @cfg {string}   cls             Extra CSS classes to add to region
41573  * 
41574  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
41575  * @cfg {string}   region  the region that it inhabits..
41576  *
41577
41578  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
41579  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
41580
41581  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
41582  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
41583  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
41584  */
41585 Roo.bootstrap.layout.Region = function(config)
41586 {
41587     this.applyConfig(config);
41588
41589     var mgr = config.mgr;
41590     var pos = config.region;
41591     config.skipConfig = true;
41592     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
41593     
41594     if (mgr.el) {
41595         this.onRender(mgr.el);   
41596     }
41597      
41598     this.visible = true;
41599     this.collapsed = false;
41600     this.unrendered_panels = [];
41601 };
41602
41603 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
41604
41605     position: '', // set by wrapper (eg. north/south etc..)
41606     unrendered_panels : null,  // unrendered panels.
41607     
41608     tabPosition : false,
41609     
41610     mgr: false, // points to 'Border'
41611     
41612     
41613     createBody : function(){
41614         /** This region's body element 
41615         * @type Roo.Element */
41616         this.bodyEl = this.el.createChild({
41617                 tag: "div",
41618                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
41619         });
41620     },
41621
41622     onRender: function(ctr, pos)
41623     {
41624         var dh = Roo.DomHelper;
41625         /** This region's container element 
41626         * @type Roo.Element */
41627         this.el = dh.append(ctr.dom, {
41628                 tag: "div",
41629                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
41630             }, true);
41631         /** This region's title element 
41632         * @type Roo.Element */
41633     
41634         this.titleEl = dh.append(this.el.dom,  {
41635                 tag: "div",
41636                 unselectable: "on",
41637                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
41638                 children:[
41639                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
41640                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
41641                 ]
41642             }, true);
41643         
41644         this.titleEl.enableDisplayMode();
41645         /** This region's title text element 
41646         * @type HTMLElement */
41647         this.titleTextEl = this.titleEl.dom.firstChild;
41648         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
41649         /*
41650         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
41651         this.closeBtn.enableDisplayMode();
41652         this.closeBtn.on("click", this.closeClicked, this);
41653         this.closeBtn.hide();
41654     */
41655         this.createBody(this.config);
41656         if(this.config.hideWhenEmpty){
41657             this.hide();
41658             this.on("paneladded", this.validateVisibility, this);
41659             this.on("panelremoved", this.validateVisibility, this);
41660         }
41661         if(this.autoScroll){
41662             this.bodyEl.setStyle("overflow", "auto");
41663         }else{
41664             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
41665         }
41666         //if(c.titlebar !== false){
41667             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
41668                 this.titleEl.hide();
41669             }else{
41670                 this.titleEl.show();
41671                 if(this.config.title){
41672                     this.titleTextEl.innerHTML = this.config.title;
41673                 }
41674             }
41675         //}
41676         if(this.config.collapsed){
41677             this.collapse(true);
41678         }
41679         if(this.config.hidden){
41680             this.hide();
41681         }
41682         
41683         if (this.unrendered_panels && this.unrendered_panels.length) {
41684             for (var i =0;i< this.unrendered_panels.length; i++) {
41685                 this.add(this.unrendered_panels[i]);
41686             }
41687             this.unrendered_panels = null;
41688             
41689         }
41690         
41691     },
41692     
41693     applyConfig : function(c)
41694     {
41695         /*
41696          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
41697             var dh = Roo.DomHelper;
41698             if(c.titlebar !== false){
41699                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
41700                 this.collapseBtn.on("click", this.collapse, this);
41701                 this.collapseBtn.enableDisplayMode();
41702                 /*
41703                 if(c.showPin === true || this.showPin){
41704                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
41705                     this.stickBtn.enableDisplayMode();
41706                     this.stickBtn.on("click", this.expand, this);
41707                     this.stickBtn.hide();
41708                 }
41709                 
41710             }
41711             */
41712             /** This region's collapsed element
41713             * @type Roo.Element */
41714             /*
41715              *
41716             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
41717                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
41718             ]}, true);
41719             
41720             if(c.floatable !== false){
41721                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
41722                this.collapsedEl.on("click", this.collapseClick, this);
41723             }
41724
41725             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
41726                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
41727                    id: "message", unselectable: "on", style:{"float":"left"}});
41728                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
41729              }
41730             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
41731             this.expandBtn.on("click", this.expand, this);
41732             
41733         }
41734         
41735         if(this.collapseBtn){
41736             this.collapseBtn.setVisible(c.collapsible == true);
41737         }
41738         
41739         this.cmargins = c.cmargins || this.cmargins ||
41740                          (this.position == "west" || this.position == "east" ?
41741                              {top: 0, left: 2, right:2, bottom: 0} :
41742                              {top: 2, left: 0, right:0, bottom: 2});
41743         */
41744         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41745         
41746         
41747         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
41748         
41749         this.autoScroll = c.autoScroll || false;
41750         
41751         
41752        
41753         
41754         this.duration = c.duration || .30;
41755         this.slideDuration = c.slideDuration || .45;
41756         this.config = c;
41757        
41758     },
41759     /**
41760      * Returns true if this region is currently visible.
41761      * @return {Boolean}
41762      */
41763     isVisible : function(){
41764         return this.visible;
41765     },
41766
41767     /**
41768      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
41769      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
41770      */
41771     //setCollapsedTitle : function(title){
41772     //    title = title || "&#160;";
41773      //   if(this.collapsedTitleTextEl){
41774       //      this.collapsedTitleTextEl.innerHTML = title;
41775        // }
41776     //},
41777
41778     getBox : function(){
41779         var b;
41780       //  if(!this.collapsed){
41781             b = this.el.getBox(false, true);
41782        // }else{
41783           //  b = this.collapsedEl.getBox(false, true);
41784         //}
41785         return b;
41786     },
41787
41788     getMargins : function(){
41789         return this.margins;
41790         //return this.collapsed ? this.cmargins : this.margins;
41791     },
41792 /*
41793     highlight : function(){
41794         this.el.addClass("x-layout-panel-dragover");
41795     },
41796
41797     unhighlight : function(){
41798         this.el.removeClass("x-layout-panel-dragover");
41799     },
41800 */
41801     updateBox : function(box)
41802     {
41803         if (!this.bodyEl) {
41804             return; // not rendered yet..
41805         }
41806         
41807         this.box = box;
41808         if(!this.collapsed){
41809             this.el.dom.style.left = box.x + "px";
41810             this.el.dom.style.top = box.y + "px";
41811             this.updateBody(box.width, box.height);
41812         }else{
41813             this.collapsedEl.dom.style.left = box.x + "px";
41814             this.collapsedEl.dom.style.top = box.y + "px";
41815             this.collapsedEl.setSize(box.width, box.height);
41816         }
41817         if(this.tabs){
41818             this.tabs.autoSizeTabs();
41819         }
41820     },
41821
41822     updateBody : function(w, h)
41823     {
41824         if(w !== null){
41825             this.el.setWidth(w);
41826             w -= this.el.getBorderWidth("rl");
41827             if(this.config.adjustments){
41828                 w += this.config.adjustments[0];
41829             }
41830         }
41831         if(h !== null && h > 0){
41832             this.el.setHeight(h);
41833             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
41834             h -= this.el.getBorderWidth("tb");
41835             if(this.config.adjustments){
41836                 h += this.config.adjustments[1];
41837             }
41838             this.bodyEl.setHeight(h);
41839             if(this.tabs){
41840                 h = this.tabs.syncHeight(h);
41841             }
41842         }
41843         if(this.panelSize){
41844             w = w !== null ? w : this.panelSize.width;
41845             h = h !== null ? h : this.panelSize.height;
41846         }
41847         if(this.activePanel){
41848             var el = this.activePanel.getEl();
41849             w = w !== null ? w : el.getWidth();
41850             h = h !== null ? h : el.getHeight();
41851             this.panelSize = {width: w, height: h};
41852             this.activePanel.setSize(w, h);
41853         }
41854         if(Roo.isIE && this.tabs){
41855             this.tabs.el.repaint();
41856         }
41857     },
41858
41859     /**
41860      * Returns the container element for this region.
41861      * @return {Roo.Element}
41862      */
41863     getEl : function(){
41864         return this.el;
41865     },
41866
41867     /**
41868      * Hides this region.
41869      */
41870     hide : function(){
41871         //if(!this.collapsed){
41872             this.el.dom.style.left = "-2000px";
41873             this.el.hide();
41874         //}else{
41875          //   this.collapsedEl.dom.style.left = "-2000px";
41876          //   this.collapsedEl.hide();
41877        // }
41878         this.visible = false;
41879         this.fireEvent("visibilitychange", this, false);
41880     },
41881
41882     /**
41883      * Shows this region if it was previously hidden.
41884      */
41885     show : function(){
41886         //if(!this.collapsed){
41887             this.el.show();
41888         //}else{
41889         //    this.collapsedEl.show();
41890        // }
41891         this.visible = true;
41892         this.fireEvent("visibilitychange", this, true);
41893     },
41894 /*
41895     closeClicked : function(){
41896         if(this.activePanel){
41897             this.remove(this.activePanel);
41898         }
41899     },
41900
41901     collapseClick : function(e){
41902         if(this.isSlid){
41903            e.stopPropagation();
41904            this.slideIn();
41905         }else{
41906            e.stopPropagation();
41907            this.slideOut();
41908         }
41909     },
41910 */
41911     /**
41912      * Collapses this region.
41913      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
41914      */
41915     /*
41916     collapse : function(skipAnim, skipCheck = false){
41917         if(this.collapsed) {
41918             return;
41919         }
41920         
41921         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
41922             
41923             this.collapsed = true;
41924             if(this.split){
41925                 this.split.el.hide();
41926             }
41927             if(this.config.animate && skipAnim !== true){
41928                 this.fireEvent("invalidated", this);
41929                 this.animateCollapse();
41930             }else{
41931                 this.el.setLocation(-20000,-20000);
41932                 this.el.hide();
41933                 this.collapsedEl.show();
41934                 this.fireEvent("collapsed", this);
41935                 this.fireEvent("invalidated", this);
41936             }
41937         }
41938         
41939     },
41940 */
41941     animateCollapse : function(){
41942         // overridden
41943     },
41944
41945     /**
41946      * Expands this region if it was previously collapsed.
41947      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
41948      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
41949      */
41950     /*
41951     expand : function(e, skipAnim){
41952         if(e) {
41953             e.stopPropagation();
41954         }
41955         if(!this.collapsed || this.el.hasActiveFx()) {
41956             return;
41957         }
41958         if(this.isSlid){
41959             this.afterSlideIn();
41960             skipAnim = true;
41961         }
41962         this.collapsed = false;
41963         if(this.config.animate && skipAnim !== true){
41964             this.animateExpand();
41965         }else{
41966             this.el.show();
41967             if(this.split){
41968                 this.split.el.show();
41969             }
41970             this.collapsedEl.setLocation(-2000,-2000);
41971             this.collapsedEl.hide();
41972             this.fireEvent("invalidated", this);
41973             this.fireEvent("expanded", this);
41974         }
41975     },
41976 */
41977     animateExpand : function(){
41978         // overridden
41979     },
41980
41981     initTabs : function()
41982     {
41983         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
41984         
41985         var ts = new Roo.bootstrap.panel.Tabs({
41986             el: this.bodyEl.dom,
41987             region : this,
41988             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
41989             disableTooltips: this.config.disableTabTips,
41990             toolbar : this.config.toolbar
41991         });
41992         
41993         if(this.config.hideTabs){
41994             ts.stripWrap.setDisplayed(false);
41995         }
41996         this.tabs = ts;
41997         ts.resizeTabs = this.config.resizeTabs === true;
41998         ts.minTabWidth = this.config.minTabWidth || 40;
41999         ts.maxTabWidth = this.config.maxTabWidth || 250;
42000         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
42001         ts.monitorResize = false;
42002         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
42003         ts.bodyEl.addClass('roo-layout-tabs-body');
42004         this.panels.each(this.initPanelAsTab, this);
42005     },
42006
42007     initPanelAsTab : function(panel){
42008         var ti = this.tabs.addTab(
42009             panel.getEl().id,
42010             panel.getTitle(),
42011             null,
42012             this.config.closeOnTab && panel.isClosable(),
42013             panel.tpl
42014         );
42015         if(panel.tabTip !== undefined){
42016             ti.setTooltip(panel.tabTip);
42017         }
42018         ti.on("activate", function(){
42019               this.setActivePanel(panel);
42020         }, this);
42021         
42022         if(this.config.closeOnTab){
42023             ti.on("beforeclose", function(t, e){
42024                 e.cancel = true;
42025                 this.remove(panel);
42026             }, this);
42027         }
42028         
42029         panel.tabItem = ti;
42030         
42031         return ti;
42032     },
42033
42034     updatePanelTitle : function(panel, title)
42035     {
42036         if(this.activePanel == panel){
42037             this.updateTitle(title);
42038         }
42039         if(this.tabs){
42040             var ti = this.tabs.getTab(panel.getEl().id);
42041             ti.setText(title);
42042             if(panel.tabTip !== undefined){
42043                 ti.setTooltip(panel.tabTip);
42044             }
42045         }
42046     },
42047
42048     updateTitle : function(title){
42049         if(this.titleTextEl && !this.config.title){
42050             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
42051         }
42052     },
42053
42054     setActivePanel : function(panel)
42055     {
42056         panel = this.getPanel(panel);
42057         if(this.activePanel && this.activePanel != panel){
42058             if(this.activePanel.setActiveState(false) === false){
42059                 return;
42060             }
42061         }
42062         this.activePanel = panel;
42063         panel.setActiveState(true);
42064         if(this.panelSize){
42065             panel.setSize(this.panelSize.width, this.panelSize.height);
42066         }
42067         if(this.closeBtn){
42068             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
42069         }
42070         this.updateTitle(panel.getTitle());
42071         if(this.tabs){
42072             this.fireEvent("invalidated", this);
42073         }
42074         this.fireEvent("panelactivated", this, panel);
42075     },
42076
42077     /**
42078      * Shows the specified panel.
42079      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
42080      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
42081      */
42082     showPanel : function(panel)
42083     {
42084         panel = this.getPanel(panel);
42085         if(panel){
42086             if(this.tabs){
42087                 var tab = this.tabs.getTab(panel.getEl().id);
42088                 if(tab.isHidden()){
42089                     this.tabs.unhideTab(tab.id);
42090                 }
42091                 tab.activate();
42092             }else{
42093                 this.setActivePanel(panel);
42094             }
42095         }
42096         return panel;
42097     },
42098
42099     /**
42100      * Get the active panel for this region.
42101      * @return {Roo.ContentPanel} The active panel or null
42102      */
42103     getActivePanel : function(){
42104         return this.activePanel;
42105     },
42106
42107     validateVisibility : function(){
42108         if(this.panels.getCount() < 1){
42109             this.updateTitle("&#160;");
42110             this.closeBtn.hide();
42111             this.hide();
42112         }else{
42113             if(!this.isVisible()){
42114                 this.show();
42115             }
42116         }
42117     },
42118
42119     /**
42120      * Adds the passed ContentPanel(s) to this region.
42121      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42122      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
42123      */
42124     add : function(panel)
42125     {
42126         if(arguments.length > 1){
42127             for(var i = 0, len = arguments.length; i < len; i++) {
42128                 this.add(arguments[i]);
42129             }
42130             return null;
42131         }
42132         
42133         // if we have not been rendered yet, then we can not really do much of this..
42134         if (!this.bodyEl) {
42135             this.unrendered_panels.push(panel);
42136             return panel;
42137         }
42138         
42139         
42140         
42141         
42142         if(this.hasPanel(panel)){
42143             this.showPanel(panel);
42144             return panel;
42145         }
42146         panel.setRegion(this);
42147         this.panels.add(panel);
42148        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
42149             // sinle panel - no tab...?? would it not be better to render it with the tabs,
42150             // and hide them... ???
42151             this.bodyEl.dom.appendChild(panel.getEl().dom);
42152             if(panel.background !== true){
42153                 this.setActivePanel(panel);
42154             }
42155             this.fireEvent("paneladded", this, panel);
42156             return panel;
42157         }
42158         */
42159         if(!this.tabs){
42160             this.initTabs();
42161         }else{
42162             this.initPanelAsTab(panel);
42163         }
42164         
42165         
42166         if(panel.background !== true){
42167             this.tabs.activate(panel.getEl().id);
42168         }
42169         this.fireEvent("paneladded", this, panel);
42170         return panel;
42171     },
42172
42173     /**
42174      * Hides the tab for the specified panel.
42175      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42176      */
42177     hidePanel : function(panel){
42178         if(this.tabs && (panel = this.getPanel(panel))){
42179             this.tabs.hideTab(panel.getEl().id);
42180         }
42181     },
42182
42183     /**
42184      * Unhides the tab for a previously hidden panel.
42185      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42186      */
42187     unhidePanel : function(panel){
42188         if(this.tabs && (panel = this.getPanel(panel))){
42189             this.tabs.unhideTab(panel.getEl().id);
42190         }
42191     },
42192
42193     clearPanels : function(){
42194         while(this.panels.getCount() > 0){
42195              this.remove(this.panels.first());
42196         }
42197     },
42198
42199     /**
42200      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
42201      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42202      * @param {Boolean} preservePanel Overrides the config preservePanel option
42203      * @return {Roo.ContentPanel} The panel that was removed
42204      */
42205     remove : function(panel, preservePanel)
42206     {
42207         panel = this.getPanel(panel);
42208         if(!panel){
42209             return null;
42210         }
42211         var e = {};
42212         this.fireEvent("beforeremove", this, panel, e);
42213         if(e.cancel === true){
42214             return null;
42215         }
42216         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
42217         var panelId = panel.getId();
42218         this.panels.removeKey(panelId);
42219         if(preservePanel){
42220             document.body.appendChild(panel.getEl().dom);
42221         }
42222         if(this.tabs){
42223             this.tabs.removeTab(panel.getEl().id);
42224         }else if (!preservePanel){
42225             this.bodyEl.dom.removeChild(panel.getEl().dom);
42226         }
42227         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
42228             var p = this.panels.first();
42229             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
42230             tempEl.appendChild(p.getEl().dom);
42231             this.bodyEl.update("");
42232             this.bodyEl.dom.appendChild(p.getEl().dom);
42233             tempEl = null;
42234             this.updateTitle(p.getTitle());
42235             this.tabs = null;
42236             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
42237             this.setActivePanel(p);
42238         }
42239         panel.setRegion(null);
42240         if(this.activePanel == panel){
42241             this.activePanel = null;
42242         }
42243         if(this.config.autoDestroy !== false && preservePanel !== true){
42244             try{panel.destroy();}catch(e){}
42245         }
42246         this.fireEvent("panelremoved", this, panel);
42247         return panel;
42248     },
42249
42250     /**
42251      * Returns the TabPanel component used by this region
42252      * @return {Roo.TabPanel}
42253      */
42254     getTabs : function(){
42255         return this.tabs;
42256     },
42257
42258     createTool : function(parentEl, className){
42259         var btn = Roo.DomHelper.append(parentEl, {
42260             tag: "div",
42261             cls: "x-layout-tools-button",
42262             children: [ {
42263                 tag: "div",
42264                 cls: "roo-layout-tools-button-inner " + className,
42265                 html: "&#160;"
42266             }]
42267         }, true);
42268         btn.addClassOnOver("roo-layout-tools-button-over");
42269         return btn;
42270     }
42271 });/*
42272  * Based on:
42273  * Ext JS Library 1.1.1
42274  * Copyright(c) 2006-2007, Ext JS, LLC.
42275  *
42276  * Originally Released Under LGPL - original licence link has changed is not relivant.
42277  *
42278  * Fork - LGPL
42279  * <script type="text/javascript">
42280  */
42281  
42282
42283
42284 /**
42285  * @class Roo.SplitLayoutRegion
42286  * @extends Roo.LayoutRegion
42287  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
42288  */
42289 Roo.bootstrap.layout.Split = function(config){
42290     this.cursor = config.cursor;
42291     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
42292 };
42293
42294 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
42295 {
42296     splitTip : "Drag to resize.",
42297     collapsibleSplitTip : "Drag to resize. Double click to hide.",
42298     useSplitTips : false,
42299
42300     applyConfig : function(config){
42301         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
42302     },
42303     
42304     onRender : function(ctr,pos) {
42305         
42306         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
42307         if(!this.config.split){
42308             return;
42309         }
42310         if(!this.split){
42311             
42312             var splitEl = Roo.DomHelper.append(ctr.dom,  {
42313                             tag: "div",
42314                             id: this.el.id + "-split",
42315                             cls: "roo-layout-split roo-layout-split-"+this.position,
42316                             html: "&#160;"
42317             });
42318             /** The SplitBar for this region 
42319             * @type Roo.SplitBar */
42320             // does not exist yet...
42321             Roo.log([this.position, this.orientation]);
42322             
42323             this.split = new Roo.bootstrap.SplitBar({
42324                 dragElement : splitEl,
42325                 resizingElement: this.el,
42326                 orientation : this.orientation
42327             });
42328             
42329             this.split.on("moved", this.onSplitMove, this);
42330             this.split.useShim = this.config.useShim === true;
42331             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
42332             if(this.useSplitTips){
42333                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
42334             }
42335             //if(config.collapsible){
42336             //    this.split.el.on("dblclick", this.collapse,  this);
42337             //}
42338         }
42339         if(typeof this.config.minSize != "undefined"){
42340             this.split.minSize = this.config.minSize;
42341         }
42342         if(typeof this.config.maxSize != "undefined"){
42343             this.split.maxSize = this.config.maxSize;
42344         }
42345         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
42346             this.hideSplitter();
42347         }
42348         
42349     },
42350
42351     getHMaxSize : function(){
42352          var cmax = this.config.maxSize || 10000;
42353          var center = this.mgr.getRegion("center");
42354          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
42355     },
42356
42357     getVMaxSize : function(){
42358          var cmax = this.config.maxSize || 10000;
42359          var center = this.mgr.getRegion("center");
42360          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
42361     },
42362
42363     onSplitMove : function(split, newSize){
42364         this.fireEvent("resized", this, newSize);
42365     },
42366     
42367     /** 
42368      * Returns the {@link Roo.SplitBar} for this region.
42369      * @return {Roo.SplitBar}
42370      */
42371     getSplitBar : function(){
42372         return this.split;
42373     },
42374     
42375     hide : function(){
42376         this.hideSplitter();
42377         Roo.bootstrap.layout.Split.superclass.hide.call(this);
42378     },
42379
42380     hideSplitter : function(){
42381         if(this.split){
42382             this.split.el.setLocation(-2000,-2000);
42383             this.split.el.hide();
42384         }
42385     },
42386
42387     show : function(){
42388         if(this.split){
42389             this.split.el.show();
42390         }
42391         Roo.bootstrap.layout.Split.superclass.show.call(this);
42392     },
42393     
42394     beforeSlide: function(){
42395         if(Roo.isGecko){// firefox overflow auto bug workaround
42396             this.bodyEl.clip();
42397             if(this.tabs) {
42398                 this.tabs.bodyEl.clip();
42399             }
42400             if(this.activePanel){
42401                 this.activePanel.getEl().clip();
42402                 
42403                 if(this.activePanel.beforeSlide){
42404                     this.activePanel.beforeSlide();
42405                 }
42406             }
42407         }
42408     },
42409     
42410     afterSlide : function(){
42411         if(Roo.isGecko){// firefox overflow auto bug workaround
42412             this.bodyEl.unclip();
42413             if(this.tabs) {
42414                 this.tabs.bodyEl.unclip();
42415             }
42416             if(this.activePanel){
42417                 this.activePanel.getEl().unclip();
42418                 if(this.activePanel.afterSlide){
42419                     this.activePanel.afterSlide();
42420                 }
42421             }
42422         }
42423     },
42424
42425     initAutoHide : function(){
42426         if(this.autoHide !== false){
42427             if(!this.autoHideHd){
42428                 var st = new Roo.util.DelayedTask(this.slideIn, this);
42429                 this.autoHideHd = {
42430                     "mouseout": function(e){
42431                         if(!e.within(this.el, true)){
42432                             st.delay(500);
42433                         }
42434                     },
42435                     "mouseover" : function(e){
42436                         st.cancel();
42437                     },
42438                     scope : this
42439                 };
42440             }
42441             this.el.on(this.autoHideHd);
42442         }
42443     },
42444
42445     clearAutoHide : function(){
42446         if(this.autoHide !== false){
42447             this.el.un("mouseout", this.autoHideHd.mouseout);
42448             this.el.un("mouseover", this.autoHideHd.mouseover);
42449         }
42450     },
42451
42452     clearMonitor : function(){
42453         Roo.get(document).un("click", this.slideInIf, this);
42454     },
42455
42456     // these names are backwards but not changed for compat
42457     slideOut : function(){
42458         if(this.isSlid || this.el.hasActiveFx()){
42459             return;
42460         }
42461         this.isSlid = true;
42462         if(this.collapseBtn){
42463             this.collapseBtn.hide();
42464         }
42465         this.closeBtnState = this.closeBtn.getStyle('display');
42466         this.closeBtn.hide();
42467         if(this.stickBtn){
42468             this.stickBtn.show();
42469         }
42470         this.el.show();
42471         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
42472         this.beforeSlide();
42473         this.el.setStyle("z-index", 10001);
42474         this.el.slideIn(this.getSlideAnchor(), {
42475             callback: function(){
42476                 this.afterSlide();
42477                 this.initAutoHide();
42478                 Roo.get(document).on("click", this.slideInIf, this);
42479                 this.fireEvent("slideshow", this);
42480             },
42481             scope: this,
42482             block: true
42483         });
42484     },
42485
42486     afterSlideIn : function(){
42487         this.clearAutoHide();
42488         this.isSlid = false;
42489         this.clearMonitor();
42490         this.el.setStyle("z-index", "");
42491         if(this.collapseBtn){
42492             this.collapseBtn.show();
42493         }
42494         this.closeBtn.setStyle('display', this.closeBtnState);
42495         if(this.stickBtn){
42496             this.stickBtn.hide();
42497         }
42498         this.fireEvent("slidehide", this);
42499     },
42500
42501     slideIn : function(cb){
42502         if(!this.isSlid || this.el.hasActiveFx()){
42503             Roo.callback(cb);
42504             return;
42505         }
42506         this.isSlid = false;
42507         this.beforeSlide();
42508         this.el.slideOut(this.getSlideAnchor(), {
42509             callback: function(){
42510                 this.el.setLeftTop(-10000, -10000);
42511                 this.afterSlide();
42512                 this.afterSlideIn();
42513                 Roo.callback(cb);
42514             },
42515             scope: this,
42516             block: true
42517         });
42518     },
42519     
42520     slideInIf : function(e){
42521         if(!e.within(this.el)){
42522             this.slideIn();
42523         }
42524     },
42525
42526     animateCollapse : function(){
42527         this.beforeSlide();
42528         this.el.setStyle("z-index", 20000);
42529         var anchor = this.getSlideAnchor();
42530         this.el.slideOut(anchor, {
42531             callback : function(){
42532                 this.el.setStyle("z-index", "");
42533                 this.collapsedEl.slideIn(anchor, {duration:.3});
42534                 this.afterSlide();
42535                 this.el.setLocation(-10000,-10000);
42536                 this.el.hide();
42537                 this.fireEvent("collapsed", this);
42538             },
42539             scope: this,
42540             block: true
42541         });
42542     },
42543
42544     animateExpand : function(){
42545         this.beforeSlide();
42546         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
42547         this.el.setStyle("z-index", 20000);
42548         this.collapsedEl.hide({
42549             duration:.1
42550         });
42551         this.el.slideIn(this.getSlideAnchor(), {
42552             callback : function(){
42553                 this.el.setStyle("z-index", "");
42554                 this.afterSlide();
42555                 if(this.split){
42556                     this.split.el.show();
42557                 }
42558                 this.fireEvent("invalidated", this);
42559                 this.fireEvent("expanded", this);
42560             },
42561             scope: this,
42562             block: true
42563         });
42564     },
42565
42566     anchors : {
42567         "west" : "left",
42568         "east" : "right",
42569         "north" : "top",
42570         "south" : "bottom"
42571     },
42572
42573     sanchors : {
42574         "west" : "l",
42575         "east" : "r",
42576         "north" : "t",
42577         "south" : "b"
42578     },
42579
42580     canchors : {
42581         "west" : "tl-tr",
42582         "east" : "tr-tl",
42583         "north" : "tl-bl",
42584         "south" : "bl-tl"
42585     },
42586
42587     getAnchor : function(){
42588         return this.anchors[this.position];
42589     },
42590
42591     getCollapseAnchor : function(){
42592         return this.canchors[this.position];
42593     },
42594
42595     getSlideAnchor : function(){
42596         return this.sanchors[this.position];
42597     },
42598
42599     getAlignAdj : function(){
42600         var cm = this.cmargins;
42601         switch(this.position){
42602             case "west":
42603                 return [0, 0];
42604             break;
42605             case "east":
42606                 return [0, 0];
42607             break;
42608             case "north":
42609                 return [0, 0];
42610             break;
42611             case "south":
42612                 return [0, 0];
42613             break;
42614         }
42615     },
42616
42617     getExpandAdj : function(){
42618         var c = this.collapsedEl, cm = this.cmargins;
42619         switch(this.position){
42620             case "west":
42621                 return [-(cm.right+c.getWidth()+cm.left), 0];
42622             break;
42623             case "east":
42624                 return [cm.right+c.getWidth()+cm.left, 0];
42625             break;
42626             case "north":
42627                 return [0, -(cm.top+cm.bottom+c.getHeight())];
42628             break;
42629             case "south":
42630                 return [0, cm.top+cm.bottom+c.getHeight()];
42631             break;
42632         }
42633     }
42634 });/*
42635  * Based on:
42636  * Ext JS Library 1.1.1
42637  * Copyright(c) 2006-2007, Ext JS, LLC.
42638  *
42639  * Originally Released Under LGPL - original licence link has changed is not relivant.
42640  *
42641  * Fork - LGPL
42642  * <script type="text/javascript">
42643  */
42644 /*
42645  * These classes are private internal classes
42646  */
42647 Roo.bootstrap.layout.Center = function(config){
42648     config.region = "center";
42649     Roo.bootstrap.layout.Region.call(this, config);
42650     this.visible = true;
42651     this.minWidth = config.minWidth || 20;
42652     this.minHeight = config.minHeight || 20;
42653 };
42654
42655 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
42656     hide : function(){
42657         // center panel can't be hidden
42658     },
42659     
42660     show : function(){
42661         // center panel can't be hidden
42662     },
42663     
42664     getMinWidth: function(){
42665         return this.minWidth;
42666     },
42667     
42668     getMinHeight: function(){
42669         return this.minHeight;
42670     }
42671 });
42672
42673
42674
42675
42676  
42677
42678
42679
42680
42681
42682
42683 Roo.bootstrap.layout.North = function(config)
42684 {
42685     config.region = 'north';
42686     config.cursor = 'n-resize';
42687     
42688     Roo.bootstrap.layout.Split.call(this, config);
42689     
42690     
42691     if(this.split){
42692         this.split.placement = Roo.bootstrap.SplitBar.TOP;
42693         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42694         this.split.el.addClass("roo-layout-split-v");
42695     }
42696     //var size = config.initialSize || config.height;
42697     //if(this.el && typeof size != "undefined"){
42698     //    this.el.setHeight(size);
42699     //}
42700 };
42701 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
42702 {
42703     orientation: Roo.bootstrap.SplitBar.VERTICAL,
42704      
42705      
42706     onRender : function(ctr, pos)
42707     {
42708         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42709         var size = this.config.initialSize || this.config.height;
42710         if(this.el && typeof size != "undefined"){
42711             this.el.setHeight(size);
42712         }
42713     
42714     },
42715     
42716     getBox : function(){
42717         if(this.collapsed){
42718             return this.collapsedEl.getBox();
42719         }
42720         var box = this.el.getBox();
42721         if(this.split){
42722             box.height += this.split.el.getHeight();
42723         }
42724         return box;
42725     },
42726     
42727     updateBox : function(box){
42728         if(this.split && !this.collapsed){
42729             box.height -= this.split.el.getHeight();
42730             this.split.el.setLeft(box.x);
42731             this.split.el.setTop(box.y+box.height);
42732             this.split.el.setWidth(box.width);
42733         }
42734         if(this.collapsed){
42735             this.updateBody(box.width, null);
42736         }
42737         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42738     }
42739 });
42740
42741
42742
42743
42744
42745 Roo.bootstrap.layout.South = function(config){
42746     config.region = 'south';
42747     config.cursor = 's-resize';
42748     Roo.bootstrap.layout.Split.call(this, config);
42749     if(this.split){
42750         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
42751         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42752         this.split.el.addClass("roo-layout-split-v");
42753     }
42754     
42755 };
42756
42757 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
42758     orientation: Roo.bootstrap.SplitBar.VERTICAL,
42759     
42760     onRender : function(ctr, pos)
42761     {
42762         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42763         var size = this.config.initialSize || this.config.height;
42764         if(this.el && typeof size != "undefined"){
42765             this.el.setHeight(size);
42766         }
42767     
42768     },
42769     
42770     getBox : function(){
42771         if(this.collapsed){
42772             return this.collapsedEl.getBox();
42773         }
42774         var box = this.el.getBox();
42775         if(this.split){
42776             var sh = this.split.el.getHeight();
42777             box.height += sh;
42778             box.y -= sh;
42779         }
42780         return box;
42781     },
42782     
42783     updateBox : function(box){
42784         if(this.split && !this.collapsed){
42785             var sh = this.split.el.getHeight();
42786             box.height -= sh;
42787             box.y += sh;
42788             this.split.el.setLeft(box.x);
42789             this.split.el.setTop(box.y-sh);
42790             this.split.el.setWidth(box.width);
42791         }
42792         if(this.collapsed){
42793             this.updateBody(box.width, null);
42794         }
42795         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42796     }
42797 });
42798
42799 Roo.bootstrap.layout.East = function(config){
42800     config.region = "east";
42801     config.cursor = "e-resize";
42802     Roo.bootstrap.layout.Split.call(this, config);
42803     if(this.split){
42804         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
42805         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
42806         this.split.el.addClass("roo-layout-split-h");
42807     }
42808     
42809 };
42810 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
42811     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
42812     
42813     onRender : function(ctr, pos)
42814     {
42815         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42816         var size = this.config.initialSize || this.config.width;
42817         if(this.el && typeof size != "undefined"){
42818             this.el.setWidth(size);
42819         }
42820     
42821     },
42822     
42823     getBox : function(){
42824         if(this.collapsed){
42825             return this.collapsedEl.getBox();
42826         }
42827         var box = this.el.getBox();
42828         if(this.split){
42829             var sw = this.split.el.getWidth();
42830             box.width += sw;
42831             box.x -= sw;
42832         }
42833         return box;
42834     },
42835
42836     updateBox : function(box){
42837         if(this.split && !this.collapsed){
42838             var sw = this.split.el.getWidth();
42839             box.width -= sw;
42840             this.split.el.setLeft(box.x);
42841             this.split.el.setTop(box.y);
42842             this.split.el.setHeight(box.height);
42843             box.x += sw;
42844         }
42845         if(this.collapsed){
42846             this.updateBody(null, box.height);
42847         }
42848         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42849     }
42850 });
42851
42852 Roo.bootstrap.layout.West = function(config){
42853     config.region = "west";
42854     config.cursor = "w-resize";
42855     
42856     Roo.bootstrap.layout.Split.call(this, config);
42857     if(this.split){
42858         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
42859         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
42860         this.split.el.addClass("roo-layout-split-h");
42861     }
42862     
42863 };
42864 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
42865     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
42866     
42867     onRender: function(ctr, pos)
42868     {
42869         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
42870         var size = this.config.initialSize || this.config.width;
42871         if(typeof size != "undefined"){
42872             this.el.setWidth(size);
42873         }
42874     },
42875     
42876     getBox : function(){
42877         if(this.collapsed){
42878             return this.collapsedEl.getBox();
42879         }
42880         var box = this.el.getBox();
42881         if (box.width == 0) {
42882             box.width = this.config.width; // kludge?
42883         }
42884         if(this.split){
42885             box.width += this.split.el.getWidth();
42886         }
42887         return box;
42888     },
42889     
42890     updateBox : function(box){
42891         if(this.split && !this.collapsed){
42892             var sw = this.split.el.getWidth();
42893             box.width -= sw;
42894             this.split.el.setLeft(box.x+box.width);
42895             this.split.el.setTop(box.y);
42896             this.split.el.setHeight(box.height);
42897         }
42898         if(this.collapsed){
42899             this.updateBody(null, box.height);
42900         }
42901         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42902     }
42903 });/*
42904  * Based on:
42905  * Ext JS Library 1.1.1
42906  * Copyright(c) 2006-2007, Ext JS, LLC.
42907  *
42908  * Originally Released Under LGPL - original licence link has changed is not relivant.
42909  *
42910  * Fork - LGPL
42911  * <script type="text/javascript">
42912  */
42913 /**
42914  * @class Roo.bootstrap.paenl.Content
42915  * @extends Roo.util.Observable
42916  * @children Roo.bootstrap.Component
42917  * @parent builder Roo.bootstrap.layout.Border
42918  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
42919  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
42920  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
42921  * @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
42922  * @cfg {Boolean}   closable      True if the panel can be closed/removed
42923  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
42924  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
42925  * @cfg {Toolbar}   toolbar       A toolbar for this panel
42926  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
42927  * @cfg {String} title          The title for this panel
42928  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
42929  * @cfg {String} url            Calls {@link #setUrl} with this value
42930  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
42931  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
42932  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
42933  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
42934  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
42935  * @cfg {Boolean} badges render the badges
42936  * @cfg {String} cls  extra classes to use  
42937  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
42938  
42939  * @constructor
42940  * Create a new ContentPanel.
42941  * @param {String/Object} config A string to set only the title or a config object
42942  
42943  */
42944 Roo.bootstrap.panel.Content = function( config){
42945     
42946     this.tpl = config.tpl || false;
42947     
42948     var el = config.el;
42949     var content = config.content;
42950
42951     if(config.autoCreate){ // xtype is available if this is called from factory
42952         el = Roo.id();
42953     }
42954     this.el = Roo.get(el);
42955     if(!this.el && config && config.autoCreate){
42956         if(typeof config.autoCreate == "object"){
42957             if(!config.autoCreate.id){
42958                 config.autoCreate.id = config.id||el;
42959             }
42960             this.el = Roo.DomHelper.append(document.body,
42961                         config.autoCreate, true);
42962         }else{
42963             var elcfg =  {
42964                 tag: "div",
42965                 cls: (config.cls || '') +
42966                     (config.background ? ' bg-' + config.background : '') +
42967                     " roo-layout-inactive-content",
42968                 id: config.id||el
42969             };
42970             if (config.iframe) {
42971                 elcfg.cn = [
42972                     {
42973                         tag : 'iframe',
42974                         style : 'border: 0px',
42975                         src : 'about:blank'
42976                     }
42977                 ];
42978             }
42979               
42980             if (config.html) {
42981                 elcfg.html = config.html;
42982                 
42983             }
42984                         
42985             this.el = Roo.DomHelper.append(document.body, elcfg , true);
42986             if (config.iframe) {
42987                 this.iframeEl = this.el.select('iframe',true).first();
42988             }
42989             
42990         }
42991     } 
42992     this.closable = false;
42993     this.loaded = false;
42994     this.active = false;
42995    
42996       
42997     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
42998         
42999         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
43000         
43001         this.wrapEl = this.el; //this.el.wrap();
43002         var ti = [];
43003         if (config.toolbar.items) {
43004             ti = config.toolbar.items ;
43005             delete config.toolbar.items ;
43006         }
43007         
43008         var nitems = [];
43009         this.toolbar.render(this.wrapEl, 'before');
43010         for(var i =0;i < ti.length;i++) {
43011           //  Roo.log(['add child', items[i]]);
43012             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43013         }
43014         this.toolbar.items = nitems;
43015         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
43016         delete config.toolbar;
43017         
43018     }
43019     /*
43020     // xtype created footer. - not sure if will work as we normally have to render first..
43021     if (this.footer && !this.footer.el && this.footer.xtype) {
43022         if (!this.wrapEl) {
43023             this.wrapEl = this.el.wrap();
43024         }
43025     
43026         this.footer.container = this.wrapEl.createChild();
43027          
43028         this.footer = Roo.factory(this.footer, Roo);
43029         
43030     }
43031     */
43032     
43033      if(typeof config == "string"){
43034         this.title = config;
43035     }else{
43036         Roo.apply(this, config);
43037     }
43038     
43039     if(this.resizeEl){
43040         this.resizeEl = Roo.get(this.resizeEl, true);
43041     }else{
43042         this.resizeEl = this.el;
43043     }
43044     // handle view.xtype
43045     
43046  
43047     
43048     
43049     this.addEvents({
43050         /**
43051          * @event activate
43052          * Fires when this panel is activated. 
43053          * @param {Roo.ContentPanel} this
43054          */
43055         "activate" : true,
43056         /**
43057          * @event deactivate
43058          * Fires when this panel is activated. 
43059          * @param {Roo.ContentPanel} this
43060          */
43061         "deactivate" : true,
43062
43063         /**
43064          * @event resize
43065          * Fires when this panel is resized if fitToFrame is true.
43066          * @param {Roo.ContentPanel} this
43067          * @param {Number} width The width after any component adjustments
43068          * @param {Number} height The height after any component adjustments
43069          */
43070         "resize" : true,
43071         
43072          /**
43073          * @event render
43074          * Fires when this tab is created
43075          * @param {Roo.ContentPanel} this
43076          */
43077         "render" : true,
43078         
43079           /**
43080          * @event scroll
43081          * Fires when this content is scrolled
43082          * @param {Roo.ContentPanel} this
43083          * @param {Event} scrollEvent
43084          */
43085         "scroll" : true
43086         
43087         
43088         
43089     });
43090     
43091
43092     
43093     
43094     if(this.autoScroll && !this.iframe){
43095         this.resizeEl.setStyle("overflow", "auto");
43096         this.resizeEl.on('scroll', this.onScroll, this);
43097     } else {
43098         // fix randome scrolling
43099         //this.el.on('scroll', function() {
43100         //    Roo.log('fix random scolling');
43101         //    this.scrollTo('top',0); 
43102         //});
43103     }
43104     content = content || this.content;
43105     if(content){
43106         this.setContent(content);
43107     }
43108     if(config && config.url){
43109         this.setUrl(this.url, this.params, this.loadOnce);
43110     }
43111     
43112     
43113     
43114     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
43115     
43116     if (this.view && typeof(this.view.xtype) != 'undefined') {
43117         this.view.el = this.el.appendChild(document.createElement("div"));
43118         this.view = Roo.factory(this.view); 
43119         this.view.render  &&  this.view.render(false, '');  
43120     }
43121     
43122     
43123     this.fireEvent('render', this);
43124 };
43125
43126 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
43127     
43128     cls : '',
43129     background : '',
43130     
43131     tabTip : '',
43132     
43133     iframe : false,
43134     iframeEl : false,
43135     
43136     /* Resize Element - use this to work out scroll etc. */
43137     resizeEl : false,
43138     
43139     setRegion : function(region){
43140         this.region = region;
43141         this.setActiveClass(region && !this.background);
43142     },
43143     
43144     
43145     setActiveClass: function(state)
43146     {
43147         if(state){
43148            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
43149            this.el.setStyle('position','relative');
43150         }else{
43151            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
43152            this.el.setStyle('position', 'absolute');
43153         } 
43154     },
43155     
43156     /**
43157      * Returns the toolbar for this Panel if one was configured. 
43158      * @return {Roo.Toolbar} 
43159      */
43160     getToolbar : function(){
43161         return this.toolbar;
43162     },
43163     
43164     setActiveState : function(active)
43165     {
43166         this.active = active;
43167         this.setActiveClass(active);
43168         if(!active){
43169             if(this.fireEvent("deactivate", this) === false){
43170                 return false;
43171             }
43172             return true;
43173         }
43174         this.fireEvent("activate", this);
43175         return true;
43176     },
43177     /**
43178      * Updates this panel's element (not for iframe)
43179      * @param {String} content The new content
43180      * @param {Boolean} loadScripts (optional) true to look for and process scripts
43181     */
43182     setContent : function(content, loadScripts){
43183         if (this.iframe) {
43184             return;
43185         }
43186         
43187         this.el.update(content, loadScripts);
43188     },
43189
43190     ignoreResize : function(w, h)
43191     {
43192         //return false; // always resize?
43193         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
43194             return true;
43195         }else{
43196             this.lastSize = {width: w, height: h};
43197             return false;
43198         }
43199     },
43200     /**
43201      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
43202      * @return {Roo.UpdateManager} The UpdateManager
43203      */
43204     getUpdateManager : function(){
43205         if (this.iframe) {
43206             return false;
43207         }
43208         return this.el.getUpdateManager();
43209     },
43210      /**
43211      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
43212      * Does not work with IFRAME contents
43213      * @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:
43214 <pre><code>
43215 panel.load({
43216     url: "your-url.php",
43217     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
43218     callback: yourFunction,
43219     scope: yourObject, //(optional scope)
43220     discardUrl: false,
43221     nocache: false,
43222     text: "Loading...",
43223     timeout: 30,
43224     scripts: false
43225 });
43226 </code></pre>
43227      
43228      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
43229      * 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.
43230      * @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}
43231      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
43232      * @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.
43233      * @return {Roo.ContentPanel} this
43234      */
43235     load : function(){
43236         
43237         if (this.iframe) {
43238             return this;
43239         }
43240         
43241         var um = this.el.getUpdateManager();
43242         um.update.apply(um, arguments);
43243         return this;
43244     },
43245
43246
43247     /**
43248      * 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.
43249      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
43250      * @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)
43251      * @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)
43252      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
43253      */
43254     setUrl : function(url, params, loadOnce){
43255         if (this.iframe) {
43256             this.iframeEl.dom.src = url;
43257             return false;
43258         }
43259         
43260         if(this.refreshDelegate){
43261             this.removeListener("activate", this.refreshDelegate);
43262         }
43263         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
43264         this.on("activate", this.refreshDelegate);
43265         return this.el.getUpdateManager();
43266     },
43267     
43268     _handleRefresh : function(url, params, loadOnce){
43269         if(!loadOnce || !this.loaded){
43270             var updater = this.el.getUpdateManager();
43271             updater.update(url, params, this._setLoaded.createDelegate(this));
43272         }
43273     },
43274     
43275     _setLoaded : function(){
43276         this.loaded = true;
43277     }, 
43278     
43279     /**
43280      * Returns this panel's id
43281      * @return {String} 
43282      */
43283     getId : function(){
43284         return this.el.id;
43285     },
43286     
43287     /** 
43288      * Returns this panel's element - used by regiosn to add.
43289      * @return {Roo.Element} 
43290      */
43291     getEl : function(){
43292         return this.wrapEl || this.el;
43293     },
43294     
43295    
43296     
43297     adjustForComponents : function(width, height)
43298     {
43299         //Roo.log('adjustForComponents ');
43300         if(this.resizeEl != this.el){
43301             width -= this.el.getFrameWidth('lr');
43302             height -= this.el.getFrameWidth('tb');
43303         }
43304         if(this.toolbar){
43305             var te = this.toolbar.getEl();
43306             te.setWidth(width);
43307             height -= te.getHeight();
43308         }
43309         if(this.footer){
43310             var te = this.footer.getEl();
43311             te.setWidth(width);
43312             height -= te.getHeight();
43313         }
43314         
43315         
43316         if(this.adjustments){
43317             width += this.adjustments[0];
43318             height += this.adjustments[1];
43319         }
43320         return {"width": width, "height": height};
43321     },
43322     
43323     setSize : function(width, height){
43324         if(this.fitToFrame && !this.ignoreResize(width, height)){
43325             if(this.fitContainer && this.resizeEl != this.el){
43326                 this.el.setSize(width, height);
43327             }
43328             var size = this.adjustForComponents(width, height);
43329             if (this.iframe) {
43330                 this.iframeEl.setSize(width,height);
43331             }
43332             
43333             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
43334             this.fireEvent('resize', this, size.width, size.height);
43335             
43336             
43337         }
43338     },
43339     
43340     /**
43341      * Returns this panel's title
43342      * @return {String} 
43343      */
43344     getTitle : function(){
43345         
43346         if (typeof(this.title) != 'object') {
43347             return this.title;
43348         }
43349         
43350         var t = '';
43351         for (var k in this.title) {
43352             if (!this.title.hasOwnProperty(k)) {
43353                 continue;
43354             }
43355             
43356             if (k.indexOf('-') >= 0) {
43357                 var s = k.split('-');
43358                 for (var i = 0; i<s.length; i++) {
43359                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
43360                 }
43361             } else {
43362                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
43363             }
43364         }
43365         return t;
43366     },
43367     
43368     /**
43369      * Set this panel's title
43370      * @param {String} title
43371      */
43372     setTitle : function(title){
43373         this.title = title;
43374         if(this.region){
43375             this.region.updatePanelTitle(this, title);
43376         }
43377     },
43378     
43379     /**
43380      * Returns true is this panel was configured to be closable
43381      * @return {Boolean} 
43382      */
43383     isClosable : function(){
43384         return this.closable;
43385     },
43386     
43387     beforeSlide : function(){
43388         this.el.clip();
43389         this.resizeEl.clip();
43390     },
43391     
43392     afterSlide : function(){
43393         this.el.unclip();
43394         this.resizeEl.unclip();
43395     },
43396     
43397     /**
43398      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
43399      *   Will fail silently if the {@link #setUrl} method has not been called.
43400      *   This does not activate the panel, just updates its content.
43401      */
43402     refresh : function(){
43403         if(this.refreshDelegate){
43404            this.loaded = false;
43405            this.refreshDelegate();
43406         }
43407     },
43408     
43409     /**
43410      * Destroys this panel
43411      */
43412     destroy : function(){
43413         this.el.removeAllListeners();
43414         var tempEl = document.createElement("span");
43415         tempEl.appendChild(this.el.dom);
43416         tempEl.innerHTML = "";
43417         this.el.remove();
43418         this.el = null;
43419     },
43420     
43421     /**
43422      * form - if the content panel contains a form - this is a reference to it.
43423      * @type {Roo.form.Form}
43424      */
43425     form : false,
43426     /**
43427      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
43428      *    This contains a reference to it.
43429      * @type {Roo.View}
43430      */
43431     view : false,
43432     
43433       /**
43434      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
43435      * <pre><code>
43436
43437 layout.addxtype({
43438        xtype : 'Form',
43439        items: [ .... ]
43440    }
43441 );
43442
43443 </code></pre>
43444      * @param {Object} cfg Xtype definition of item to add.
43445      */
43446     
43447     
43448     getChildContainer: function () {
43449         return this.getEl();
43450     },
43451     
43452     
43453     onScroll : function(e)
43454     {
43455         this.fireEvent('scroll', this, e);
43456     }
43457     
43458     
43459     /*
43460         var  ret = new Roo.factory(cfg);
43461         return ret;
43462         
43463         
43464         // add form..
43465         if (cfg.xtype.match(/^Form$/)) {
43466             
43467             var el;
43468             //if (this.footer) {
43469             //    el = this.footer.container.insertSibling(false, 'before');
43470             //} else {
43471                 el = this.el.createChild();
43472             //}
43473
43474             this.form = new  Roo.form.Form(cfg);
43475             
43476             
43477             if ( this.form.allItems.length) {
43478                 this.form.render(el.dom);
43479             }
43480             return this.form;
43481         }
43482         // should only have one of theses..
43483         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
43484             // views.. should not be just added - used named prop 'view''
43485             
43486             cfg.el = this.el.appendChild(document.createElement("div"));
43487             // factory?
43488             
43489             var ret = new Roo.factory(cfg);
43490              
43491              ret.render && ret.render(false, ''); // render blank..
43492             this.view = ret;
43493             return ret;
43494         }
43495         return false;
43496     }
43497     \*/
43498 });
43499  
43500 /**
43501  * @class Roo.bootstrap.panel.Grid
43502  * @extends Roo.bootstrap.panel.Content
43503  * @constructor
43504  * Create a new GridPanel.
43505  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
43506  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
43507  * @param {Object} config A the config object
43508   
43509  */
43510
43511
43512
43513 Roo.bootstrap.panel.Grid = function(config)
43514 {
43515     
43516       
43517     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
43518         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
43519
43520     config.el = this.wrapper;
43521     //this.el = this.wrapper;
43522     
43523       if (config.container) {
43524         // ctor'ed from a Border/panel.grid
43525         
43526         
43527         this.wrapper.setStyle("overflow", "hidden");
43528         this.wrapper.addClass('roo-grid-container');
43529
43530     }
43531     
43532     
43533     if(config.toolbar){
43534         var tool_el = this.wrapper.createChild();    
43535         this.toolbar = Roo.factory(config.toolbar);
43536         var ti = [];
43537         if (config.toolbar.items) {
43538             ti = config.toolbar.items ;
43539             delete config.toolbar.items ;
43540         }
43541         
43542         var nitems = [];
43543         this.toolbar.render(tool_el);
43544         for(var i =0;i < ti.length;i++) {
43545           //  Roo.log(['add child', items[i]]);
43546             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43547         }
43548         this.toolbar.items = nitems;
43549         
43550         delete config.toolbar;
43551     }
43552     
43553     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
43554     config.grid.scrollBody = true;;
43555     config.grid.monitorWindowResize = false; // turn off autosizing
43556     config.grid.autoHeight = false;
43557     config.grid.autoWidth = false;
43558     
43559     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
43560     
43561     if (config.background) {
43562         // render grid on panel activation (if panel background)
43563         this.on('activate', function(gp) {
43564             if (!gp.grid.rendered) {
43565                 gp.grid.render(this.wrapper);
43566                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
43567             }
43568         });
43569             
43570     } else {
43571         this.grid.render(this.wrapper);
43572         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
43573
43574     }
43575     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
43576     // ??? needed ??? config.el = this.wrapper;
43577     
43578     
43579     
43580   
43581     // xtype created footer. - not sure if will work as we normally have to render first..
43582     if (this.footer && !this.footer.el && this.footer.xtype) {
43583         
43584         var ctr = this.grid.getView().getFooterPanel(true);
43585         this.footer.dataSource = this.grid.dataSource;
43586         this.footer = Roo.factory(this.footer, Roo);
43587         this.footer.render(ctr);
43588         
43589     }
43590     
43591     
43592     
43593     
43594      
43595 };
43596
43597 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
43598 {
43599   
43600     getId : function(){
43601         return this.grid.id;
43602     },
43603     
43604     /**
43605      * Returns the grid for this panel
43606      * @return {Roo.bootstrap.Table} 
43607      */
43608     getGrid : function(){
43609         return this.grid;    
43610     },
43611     
43612     setSize : function(width, height)
43613     {
43614      
43615         //if(!this.ignoreResize(width, height)){
43616             var grid = this.grid;
43617             var size = this.adjustForComponents(width, height);
43618             // tfoot is not a footer?
43619           
43620             
43621             var gridel = grid.getGridEl();
43622             gridel.setSize(size.width, size.height);
43623             
43624             var tbd = grid.getGridEl().select('tbody', true).first();
43625             var thd = grid.getGridEl().select('thead',true).first();
43626             var tbf= grid.getGridEl().select('tfoot', true).first();
43627
43628             if (tbf) {
43629                 size.height -= tbf.getHeight();
43630             }
43631             if (thd) {
43632                 size.height -= thd.getHeight();
43633             }
43634             
43635             tbd.setSize(size.width, size.height );
43636             // this is for the account management tab -seems to work there.
43637             var thd = grid.getGridEl().select('thead',true).first();
43638             //if (tbd) {
43639             //    tbd.setSize(size.width, size.height - thd.getHeight());
43640             //}
43641              
43642             grid.autoSize();
43643         //}
43644    
43645     },
43646      
43647     
43648     
43649     beforeSlide : function(){
43650         this.grid.getView().scroller.clip();
43651     },
43652     
43653     afterSlide : function(){
43654         this.grid.getView().scroller.unclip();
43655     },
43656     
43657     destroy : function(){
43658         this.grid.destroy();
43659         delete this.grid;
43660         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
43661     }
43662 });
43663
43664 /**
43665  * @class Roo.bootstrap.panel.Nest
43666  * @extends Roo.bootstrap.panel.Content
43667  * @constructor
43668  * Create a new Panel, that can contain a layout.Border.
43669  * 
43670  * 
43671  * @param {String/Object} config A string to set only the title or a config object
43672  */
43673 Roo.bootstrap.panel.Nest = function(config)
43674 {
43675     // construct with only one argument..
43676     /* FIXME - implement nicer consturctors
43677     if (layout.layout) {
43678         config = layout;
43679         layout = config.layout;
43680         delete config.layout;
43681     }
43682     if (layout.xtype && !layout.getEl) {
43683         // then layout needs constructing..
43684         layout = Roo.factory(layout, Roo);
43685     }
43686     */
43687     
43688     config.el =  config.layout.getEl();
43689     
43690     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
43691     
43692     config.layout.monitorWindowResize = false; // turn off autosizing
43693     this.layout = config.layout;
43694     this.layout.getEl().addClass("roo-layout-nested-layout");
43695     this.layout.parent = this;
43696     
43697     
43698     
43699     
43700 };
43701
43702 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
43703     /**
43704     * @cfg {Roo.BorderLayout} layout The layout for this panel
43705     */
43706     layout : false,
43707
43708     setSize : function(width, height){
43709         if(!this.ignoreResize(width, height)){
43710             var size = this.adjustForComponents(width, height);
43711             var el = this.layout.getEl();
43712             if (size.height < 1) {
43713                 el.setWidth(size.width);   
43714             } else {
43715                 el.setSize(size.width, size.height);
43716             }
43717             var touch = el.dom.offsetWidth;
43718             this.layout.layout();
43719             // ie requires a double layout on the first pass
43720             if(Roo.isIE && !this.initialized){
43721                 this.initialized = true;
43722                 this.layout.layout();
43723             }
43724         }
43725     },
43726     
43727     // activate all subpanels if not currently active..
43728     
43729     setActiveState : function(active){
43730         this.active = active;
43731         this.setActiveClass(active);
43732         
43733         if(!active){
43734             this.fireEvent("deactivate", this);
43735             return;
43736         }
43737         
43738         this.fireEvent("activate", this);
43739         // not sure if this should happen before or after..
43740         if (!this.layout) {
43741             return; // should not happen..
43742         }
43743         var reg = false;
43744         for (var r in this.layout.regions) {
43745             reg = this.layout.getRegion(r);
43746             if (reg.getActivePanel()) {
43747                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
43748                 reg.setActivePanel(reg.getActivePanel());
43749                 continue;
43750             }
43751             if (!reg.panels.length) {
43752                 continue;
43753             }
43754             reg.showPanel(reg.getPanel(0));
43755         }
43756         
43757         
43758         
43759         
43760     },
43761     
43762     /**
43763      * Returns the nested BorderLayout for this panel
43764      * @return {Roo.BorderLayout} 
43765      */
43766     getLayout : function(){
43767         return this.layout;
43768     },
43769     
43770      /**
43771      * Adds a xtype elements to the layout of the nested panel
43772      * <pre><code>
43773
43774 panel.addxtype({
43775        xtype : 'ContentPanel',
43776        region: 'west',
43777        items: [ .... ]
43778    }
43779 );
43780
43781 panel.addxtype({
43782         xtype : 'NestedLayoutPanel',
43783         region: 'west',
43784         layout: {
43785            center: { },
43786            west: { }   
43787         },
43788         items : [ ... list of content panels or nested layout panels.. ]
43789    }
43790 );
43791 </code></pre>
43792      * @param {Object} cfg Xtype definition of item to add.
43793      */
43794     addxtype : function(cfg) {
43795         return this.layout.addxtype(cfg);
43796     
43797     }
43798 });/*
43799  * Based on:
43800  * Ext JS Library 1.1.1
43801  * Copyright(c) 2006-2007, Ext JS, LLC.
43802  *
43803  * Originally Released Under LGPL - original licence link has changed is not relivant.
43804  *
43805  * Fork - LGPL
43806  * <script type="text/javascript">
43807  */
43808 /**
43809  * @class Roo.TabPanel
43810  * @extends Roo.util.Observable
43811  * A lightweight tab container.
43812  * <br><br>
43813  * Usage:
43814  * <pre><code>
43815 // basic tabs 1, built from existing content
43816 var tabs = new Roo.TabPanel("tabs1");
43817 tabs.addTab("script", "View Script");
43818 tabs.addTab("markup", "View Markup");
43819 tabs.activate("script");
43820
43821 // more advanced tabs, built from javascript
43822 var jtabs = new Roo.TabPanel("jtabs");
43823 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
43824
43825 // set up the UpdateManager
43826 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
43827 var updater = tab2.getUpdateManager();
43828 updater.setDefaultUrl("ajax1.htm");
43829 tab2.on('activate', updater.refresh, updater, true);
43830
43831 // Use setUrl for Ajax loading
43832 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
43833 tab3.setUrl("ajax2.htm", null, true);
43834
43835 // Disabled tab
43836 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
43837 tab4.disable();
43838
43839 jtabs.activate("jtabs-1");
43840  * </code></pre>
43841  * @constructor
43842  * Create a new TabPanel.
43843  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
43844  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
43845  */
43846 Roo.bootstrap.panel.Tabs = function(config){
43847     /**
43848     * The container element for this TabPanel.
43849     * @type Roo.Element
43850     */
43851     this.el = Roo.get(config.el);
43852     delete config.el;
43853     if(config){
43854         if(typeof config == "boolean"){
43855             this.tabPosition = config ? "bottom" : "top";
43856         }else{
43857             Roo.apply(this, config);
43858         }
43859     }
43860     
43861     if(this.tabPosition == "bottom"){
43862         // if tabs are at the bottom = create the body first.
43863         this.bodyEl = Roo.get(this.createBody(this.el.dom));
43864         this.el.addClass("roo-tabs-bottom");
43865     }
43866     // next create the tabs holders
43867     
43868     if (this.tabPosition == "west"){
43869         
43870         var reg = this.region; // fake it..
43871         while (reg) {
43872             if (!reg.mgr.parent) {
43873                 break;
43874             }
43875             reg = reg.mgr.parent.region;
43876         }
43877         Roo.log("got nest?");
43878         Roo.log(reg);
43879         if (reg.mgr.getRegion('west')) {
43880             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
43881             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
43882             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
43883             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
43884             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
43885         
43886             
43887         }
43888         
43889         
43890     } else {
43891      
43892         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
43893         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
43894         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
43895         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
43896     }
43897     
43898     
43899     if(Roo.isIE){
43900         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
43901     }
43902     
43903     // finally - if tabs are at the top, then create the body last..
43904     if(this.tabPosition != "bottom"){
43905         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
43906          * @type Roo.Element
43907          */
43908         this.bodyEl = Roo.get(this.createBody(this.el.dom));
43909         this.el.addClass("roo-tabs-top");
43910     }
43911     this.items = [];
43912
43913     this.bodyEl.setStyle("position", "relative");
43914
43915     this.active = null;
43916     this.activateDelegate = this.activate.createDelegate(this);
43917
43918     this.addEvents({
43919         /**
43920          * @event tabchange
43921          * Fires when the active tab changes
43922          * @param {Roo.TabPanel} this
43923          * @param {Roo.TabPanelItem} activePanel The new active tab
43924          */
43925         "tabchange": true,
43926         /**
43927          * @event beforetabchange
43928          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
43929          * @param {Roo.TabPanel} this
43930          * @param {Object} e Set cancel to true on this object to cancel the tab change
43931          * @param {Roo.TabPanelItem} tab The tab being changed to
43932          */
43933         "beforetabchange" : true
43934     });
43935
43936     Roo.EventManager.onWindowResize(this.onResize, this);
43937     this.cpad = this.el.getPadding("lr");
43938     this.hiddenCount = 0;
43939
43940
43941     // toolbar on the tabbar support...
43942     if (this.toolbar) {
43943         alert("no toolbar support yet");
43944         this.toolbar  = false;
43945         /*
43946         var tcfg = this.toolbar;
43947         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
43948         this.toolbar = new Roo.Toolbar(tcfg);
43949         if (Roo.isSafari) {
43950             var tbl = tcfg.container.child('table', true);
43951             tbl.setAttribute('width', '100%');
43952         }
43953         */
43954         
43955     }
43956    
43957
43958
43959     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
43960 };
43961
43962 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
43963     /*
43964      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
43965      */
43966     tabPosition : "top",
43967     /*
43968      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
43969      */
43970     currentTabWidth : 0,
43971     /*
43972      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
43973      */
43974     minTabWidth : 40,
43975     /*
43976      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
43977      */
43978     maxTabWidth : 250,
43979     /*
43980      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
43981      */
43982     preferredTabWidth : 175,
43983     /*
43984      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
43985      */
43986     resizeTabs : false,
43987     /*
43988      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
43989      */
43990     monitorResize : true,
43991     /*
43992      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
43993      */
43994     toolbar : false,  // set by caller..
43995     
43996     region : false, /// set by caller
43997     
43998     disableTooltips : true, // not used yet...
43999
44000     /**
44001      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
44002      * @param {String} id The id of the div to use <b>or create</b>
44003      * @param {String} text The text for the tab
44004      * @param {String} content (optional) Content to put in the TabPanelItem body
44005      * @param {Boolean} closable (optional) True to create a close icon on the tab
44006      * @return {Roo.TabPanelItem} The created TabPanelItem
44007      */
44008     addTab : function(id, text, content, closable, tpl)
44009     {
44010         var item = new Roo.bootstrap.panel.TabItem({
44011             panel: this,
44012             id : id,
44013             text : text,
44014             closable : closable,
44015             tpl : tpl
44016         });
44017         this.addTabItem(item);
44018         if(content){
44019             item.setContent(content);
44020         }
44021         return item;
44022     },
44023
44024     /**
44025      * Returns the {@link Roo.TabPanelItem} with the specified id/index
44026      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
44027      * @return {Roo.TabPanelItem}
44028      */
44029     getTab : function(id){
44030         return this.items[id];
44031     },
44032
44033     /**
44034      * Hides the {@link Roo.TabPanelItem} with the specified id/index
44035      * @param {String/Number} id The id or index of the TabPanelItem to hide.
44036      */
44037     hideTab : function(id){
44038         var t = this.items[id];
44039         if(!t.isHidden()){
44040            t.setHidden(true);
44041            this.hiddenCount++;
44042            this.autoSizeTabs();
44043         }
44044     },
44045
44046     /**
44047      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
44048      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
44049      */
44050     unhideTab : function(id){
44051         var t = this.items[id];
44052         if(t.isHidden()){
44053            t.setHidden(false);
44054            this.hiddenCount--;
44055            this.autoSizeTabs();
44056         }
44057     },
44058
44059     /**
44060      * Adds an existing {@link Roo.TabPanelItem}.
44061      * @param {Roo.TabPanelItem} item The TabPanelItem to add
44062      */
44063     addTabItem : function(item)
44064     {
44065         this.items[item.id] = item;
44066         this.items.push(item);
44067         this.autoSizeTabs();
44068       //  if(this.resizeTabs){
44069     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
44070   //         this.autoSizeTabs();
44071 //        }else{
44072 //            item.autoSize();
44073        // }
44074     },
44075
44076     /**
44077      * Removes a {@link Roo.TabPanelItem}.
44078      * @param {String/Number} id The id or index of the TabPanelItem to remove.
44079      */
44080     removeTab : function(id){
44081         var items = this.items;
44082         var tab = items[id];
44083         if(!tab) { return; }
44084         var index = items.indexOf(tab);
44085         if(this.active == tab && items.length > 1){
44086             var newTab = this.getNextAvailable(index);
44087             if(newTab) {
44088                 newTab.activate();
44089             }
44090         }
44091         this.stripEl.dom.removeChild(tab.pnode.dom);
44092         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
44093             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
44094         }
44095         items.splice(index, 1);
44096         delete this.items[tab.id];
44097         tab.fireEvent("close", tab);
44098         tab.purgeListeners();
44099         this.autoSizeTabs();
44100     },
44101
44102     getNextAvailable : function(start){
44103         var items = this.items;
44104         var index = start;
44105         // look for a next tab that will slide over to
44106         // replace the one being removed
44107         while(index < items.length){
44108             var item = items[++index];
44109             if(item && !item.isHidden()){
44110                 return item;
44111             }
44112         }
44113         // if one isn't found select the previous tab (on the left)
44114         index = start;
44115         while(index >= 0){
44116             var item = items[--index];
44117             if(item && !item.isHidden()){
44118                 return item;
44119             }
44120         }
44121         return null;
44122     },
44123
44124     /**
44125      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
44126      * @param {String/Number} id The id or index of the TabPanelItem to disable.
44127      */
44128     disableTab : function(id){
44129         var tab = this.items[id];
44130         if(tab && this.active != tab){
44131             tab.disable();
44132         }
44133     },
44134
44135     /**
44136      * Enables a {@link Roo.TabPanelItem} that is disabled.
44137      * @param {String/Number} id The id or index of the TabPanelItem to enable.
44138      */
44139     enableTab : function(id){
44140         var tab = this.items[id];
44141         tab.enable();
44142     },
44143
44144     /**
44145      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
44146      * @param {String/Number} id The id or index of the TabPanelItem to activate.
44147      * @return {Roo.TabPanelItem} The TabPanelItem.
44148      */
44149     activate : function(id)
44150     {
44151         //Roo.log('activite:'  + id);
44152         
44153         var tab = this.items[id];
44154         if(!tab){
44155             return null;
44156         }
44157         if(tab == this.active || tab.disabled){
44158             return tab;
44159         }
44160         var e = {};
44161         this.fireEvent("beforetabchange", this, e, tab);
44162         if(e.cancel !== true && !tab.disabled){
44163             if(this.active){
44164                 this.active.hide();
44165             }
44166             this.active = this.items[id];
44167             this.active.show();
44168             this.fireEvent("tabchange", this, this.active);
44169         }
44170         return tab;
44171     },
44172
44173     /**
44174      * Gets the active {@link Roo.TabPanelItem}.
44175      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
44176      */
44177     getActiveTab : function(){
44178         return this.active;
44179     },
44180
44181     /**
44182      * Updates the tab body element to fit the height of the container element
44183      * for overflow scrolling
44184      * @param {Number} targetHeight (optional) Override the starting height from the elements height
44185      */
44186     syncHeight : function(targetHeight){
44187         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44188         var bm = this.bodyEl.getMargins();
44189         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
44190         this.bodyEl.setHeight(newHeight);
44191         return newHeight;
44192     },
44193
44194     onResize : function(){
44195         if(this.monitorResize){
44196             this.autoSizeTabs();
44197         }
44198     },
44199
44200     /**
44201      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
44202      */
44203     beginUpdate : function(){
44204         this.updating = true;
44205     },
44206
44207     /**
44208      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
44209      */
44210     endUpdate : function(){
44211         this.updating = false;
44212         this.autoSizeTabs();
44213     },
44214
44215     /**
44216      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
44217      */
44218     autoSizeTabs : function()
44219     {
44220         var count = this.items.length;
44221         var vcount = count - this.hiddenCount;
44222         
44223         if (vcount < 2) {
44224             this.stripEl.hide();
44225         } else {
44226             this.stripEl.show();
44227         }
44228         
44229         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
44230             return;
44231         }
44232         
44233         
44234         var w = Math.max(this.el.getWidth() - this.cpad, 10);
44235         var availWidth = Math.floor(w / vcount);
44236         var b = this.stripBody;
44237         if(b.getWidth() > w){
44238             var tabs = this.items;
44239             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
44240             if(availWidth < this.minTabWidth){
44241                 /*if(!this.sleft){    // incomplete scrolling code
44242                     this.createScrollButtons();
44243                 }
44244                 this.showScroll();
44245                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
44246             }
44247         }else{
44248             if(this.currentTabWidth < this.preferredTabWidth){
44249                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
44250             }
44251         }
44252     },
44253
44254     /**
44255      * Returns the number of tabs in this TabPanel.
44256      * @return {Number}
44257      */
44258      getCount : function(){
44259          return this.items.length;
44260      },
44261
44262     /**
44263      * Resizes all the tabs to the passed width
44264      * @param {Number} The new width
44265      */
44266     setTabWidth : function(width){
44267         this.currentTabWidth = width;
44268         for(var i = 0, len = this.items.length; i < len; i++) {
44269                 if(!this.items[i].isHidden()) {
44270                 this.items[i].setWidth(width);
44271             }
44272         }
44273     },
44274
44275     /**
44276      * Destroys this TabPanel
44277      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
44278      */
44279     destroy : function(removeEl){
44280         Roo.EventManager.removeResizeListener(this.onResize, this);
44281         for(var i = 0, len = this.items.length; i < len; i++){
44282             this.items[i].purgeListeners();
44283         }
44284         if(removeEl === true){
44285             this.el.update("");
44286             this.el.remove();
44287         }
44288     },
44289     
44290     createStrip : function(container)
44291     {
44292         var strip = document.createElement("nav");
44293         strip.className = Roo.bootstrap.version == 4 ?
44294             "navbar-light bg-light" : 
44295             "navbar navbar-default"; //"x-tabs-wrap";
44296         container.appendChild(strip);
44297         return strip;
44298     },
44299     
44300     createStripList : function(strip)
44301     {
44302         // div wrapper for retard IE
44303         // returns the "tr" element.
44304         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
44305         //'<div class="x-tabs-strip-wrap">'+
44306           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
44307           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
44308         return strip.firstChild; //.firstChild.firstChild.firstChild;
44309     },
44310     createBody : function(container)
44311     {
44312         var body = document.createElement("div");
44313         Roo.id(body, "tab-body");
44314         //Roo.fly(body).addClass("x-tabs-body");
44315         Roo.fly(body).addClass("tab-content");
44316         container.appendChild(body);
44317         return body;
44318     },
44319     createItemBody :function(bodyEl, id){
44320         var body = Roo.getDom(id);
44321         if(!body){
44322             body = document.createElement("div");
44323             body.id = id;
44324         }
44325         //Roo.fly(body).addClass("x-tabs-item-body");
44326         Roo.fly(body).addClass("tab-pane");
44327          bodyEl.insertBefore(body, bodyEl.firstChild);
44328         return body;
44329     },
44330     /** @private */
44331     createStripElements :  function(stripEl, text, closable, tpl)
44332     {
44333         var td = document.createElement("li"); // was td..
44334         td.className = 'nav-item';
44335         
44336         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
44337         
44338         
44339         stripEl.appendChild(td);
44340         /*if(closable){
44341             td.className = "x-tabs-closable";
44342             if(!this.closeTpl){
44343                 this.closeTpl = new Roo.Template(
44344                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44345                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
44346                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
44347                 );
44348             }
44349             var el = this.closeTpl.overwrite(td, {"text": text});
44350             var close = el.getElementsByTagName("div")[0];
44351             var inner = el.getElementsByTagName("em")[0];
44352             return {"el": el, "close": close, "inner": inner};
44353         } else {
44354         */
44355         // not sure what this is..
44356 //            if(!this.tabTpl){
44357                 //this.tabTpl = new Roo.Template(
44358                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44359                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
44360                 //);
44361 //                this.tabTpl = new Roo.Template(
44362 //                   '<a href="#">' +
44363 //                   '<span unselectable="on"' +
44364 //                            (this.disableTooltips ? '' : ' title="{text}"') +
44365 //                            ' >{text}</span></a>'
44366 //                );
44367 //                
44368 //            }
44369
44370
44371             var template = tpl || this.tabTpl || false;
44372             
44373             if(!template){
44374                 template =  new Roo.Template(
44375                         Roo.bootstrap.version == 4 ? 
44376                             (
44377                                 '<a class="nav-link" href="#" unselectable="on"' +
44378                                      (this.disableTooltips ? '' : ' title="{text}"') +
44379                                      ' >{text}</a>'
44380                             ) : (
44381                                 '<a class="nav-link" href="#">' +
44382                                 '<span unselectable="on"' +
44383                                          (this.disableTooltips ? '' : ' title="{text}"') +
44384                                     ' >{text}</span></a>'
44385                             )
44386                 );
44387             }
44388             
44389             switch (typeof(template)) {
44390                 case 'object' :
44391                     break;
44392                 case 'string' :
44393                     template = new Roo.Template(template);
44394                     break;
44395                 default :
44396                     break;
44397             }
44398             
44399             var el = template.overwrite(td, {"text": text});
44400             
44401             var inner = el.getElementsByTagName("span")[0];
44402             
44403             return {"el": el, "inner": inner};
44404             
44405     }
44406         
44407     
44408 });
44409
44410 /**
44411  * @class Roo.TabPanelItem
44412  * @extends Roo.util.Observable
44413  * Represents an individual item (tab plus body) in a TabPanel.
44414  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
44415  * @param {String} id The id of this TabPanelItem
44416  * @param {String} text The text for the tab of this TabPanelItem
44417  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
44418  */
44419 Roo.bootstrap.panel.TabItem = function(config){
44420     /**
44421      * The {@link Roo.TabPanel} this TabPanelItem belongs to
44422      * @type Roo.TabPanel
44423      */
44424     this.tabPanel = config.panel;
44425     /**
44426      * The id for this TabPanelItem
44427      * @type String
44428      */
44429     this.id = config.id;
44430     /** @private */
44431     this.disabled = false;
44432     /** @private */
44433     this.text = config.text;
44434     /** @private */
44435     this.loaded = false;
44436     this.closable = config.closable;
44437
44438     /**
44439      * The body element for this TabPanelItem.
44440      * @type Roo.Element
44441      */
44442     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
44443     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
44444     this.bodyEl.setStyle("display", "block");
44445     this.bodyEl.setStyle("zoom", "1");
44446     //this.hideAction();
44447
44448     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
44449     /** @private */
44450     this.el = Roo.get(els.el);
44451     this.inner = Roo.get(els.inner, true);
44452      this.textEl = Roo.bootstrap.version == 4 ?
44453         this.el : Roo.get(this.el.dom.firstChild, true);
44454
44455     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
44456     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
44457
44458     
44459 //    this.el.on("mousedown", this.onTabMouseDown, this);
44460     this.el.on("click", this.onTabClick, this);
44461     /** @private */
44462     if(config.closable){
44463         var c = Roo.get(els.close, true);
44464         c.dom.title = this.closeText;
44465         c.addClassOnOver("close-over");
44466         c.on("click", this.closeClick, this);
44467      }
44468
44469     this.addEvents({
44470          /**
44471          * @event activate
44472          * Fires when this tab becomes the active tab.
44473          * @param {Roo.TabPanel} tabPanel The parent TabPanel
44474          * @param {Roo.TabPanelItem} this
44475          */
44476         "activate": true,
44477         /**
44478          * @event beforeclose
44479          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
44480          * @param {Roo.TabPanelItem} this
44481          * @param {Object} e Set cancel to true on this object to cancel the close.
44482          */
44483         "beforeclose": true,
44484         /**
44485          * @event close
44486          * Fires when this tab is closed.
44487          * @param {Roo.TabPanelItem} this
44488          */
44489          "close": true,
44490         /**
44491          * @event deactivate
44492          * Fires when this tab is no longer the active tab.
44493          * @param {Roo.TabPanel} tabPanel The parent TabPanel
44494          * @param {Roo.TabPanelItem} this
44495          */
44496          "deactivate" : true
44497     });
44498     this.hidden = false;
44499
44500     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
44501 };
44502
44503 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
44504            {
44505     purgeListeners : function(){
44506        Roo.util.Observable.prototype.purgeListeners.call(this);
44507        this.el.removeAllListeners();
44508     },
44509     /**
44510      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
44511      */
44512     show : function(){
44513         this.status_node.addClass("active");
44514         this.showAction();
44515         if(Roo.isOpera){
44516             this.tabPanel.stripWrap.repaint();
44517         }
44518         this.fireEvent("activate", this.tabPanel, this);
44519     },
44520
44521     /**
44522      * Returns true if this tab is the active tab.
44523      * @return {Boolean}
44524      */
44525     isActive : function(){
44526         return this.tabPanel.getActiveTab() == this;
44527     },
44528
44529     /**
44530      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
44531      */
44532     hide : function(){
44533         this.status_node.removeClass("active");
44534         this.hideAction();
44535         this.fireEvent("deactivate", this.tabPanel, this);
44536     },
44537
44538     hideAction : function(){
44539         this.bodyEl.hide();
44540         this.bodyEl.setStyle("position", "absolute");
44541         this.bodyEl.setLeft("-20000px");
44542         this.bodyEl.setTop("-20000px");
44543     },
44544
44545     showAction : function(){
44546         this.bodyEl.setStyle("position", "relative");
44547         this.bodyEl.setTop("");
44548         this.bodyEl.setLeft("");
44549         this.bodyEl.show();
44550     },
44551
44552     /**
44553      * Set the tooltip for the tab.
44554      * @param {String} tooltip The tab's tooltip
44555      */
44556     setTooltip : function(text){
44557         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
44558             this.textEl.dom.qtip = text;
44559             this.textEl.dom.removeAttribute('title');
44560         }else{
44561             this.textEl.dom.title = text;
44562         }
44563     },
44564
44565     onTabClick : function(e){
44566         e.preventDefault();
44567         this.tabPanel.activate(this.id);
44568     },
44569
44570     onTabMouseDown : function(e){
44571         e.preventDefault();
44572         this.tabPanel.activate(this.id);
44573     },
44574 /*
44575     getWidth : function(){
44576         return this.inner.getWidth();
44577     },
44578
44579     setWidth : function(width){
44580         var iwidth = width - this.linode.getPadding("lr");
44581         this.inner.setWidth(iwidth);
44582         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
44583         this.linode.setWidth(width);
44584     },
44585 */
44586     /**
44587      * Show or hide the tab
44588      * @param {Boolean} hidden True to hide or false to show.
44589      */
44590     setHidden : function(hidden){
44591         this.hidden = hidden;
44592         this.linode.setStyle("display", hidden ? "none" : "");
44593     },
44594
44595     /**
44596      * Returns true if this tab is "hidden"
44597      * @return {Boolean}
44598      */
44599     isHidden : function(){
44600         return this.hidden;
44601     },
44602
44603     /**
44604      * Returns the text for this tab
44605      * @return {String}
44606      */
44607     getText : function(){
44608         return this.text;
44609     },
44610     /*
44611     autoSize : function(){
44612         //this.el.beginMeasure();
44613         this.textEl.setWidth(1);
44614         /*
44615          *  #2804 [new] Tabs in Roojs
44616          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
44617          */
44618         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
44619         //this.el.endMeasure();
44620     //},
44621
44622     /**
44623      * Sets the text for the tab (Note: this also sets the tooltip text)
44624      * @param {String} text The tab's text and tooltip
44625      */
44626     setText : function(text){
44627         this.text = text;
44628         this.textEl.update(text);
44629         this.setTooltip(text);
44630         //if(!this.tabPanel.resizeTabs){
44631         //    this.autoSize();
44632         //}
44633     },
44634     /**
44635      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
44636      */
44637     activate : function(){
44638         this.tabPanel.activate(this.id);
44639     },
44640
44641     /**
44642      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
44643      */
44644     disable : function(){
44645         if(this.tabPanel.active != this){
44646             this.disabled = true;
44647             this.status_node.addClass("disabled");
44648         }
44649     },
44650
44651     /**
44652      * Enables this TabPanelItem if it was previously disabled.
44653      */
44654     enable : function(){
44655         this.disabled = false;
44656         this.status_node.removeClass("disabled");
44657     },
44658
44659     /**
44660      * Sets the content for this TabPanelItem.
44661      * @param {String} content The content
44662      * @param {Boolean} loadScripts true to look for and load scripts
44663      */
44664     setContent : function(content, loadScripts){
44665         this.bodyEl.update(content, loadScripts);
44666     },
44667
44668     /**
44669      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
44670      * @return {Roo.UpdateManager} The UpdateManager
44671      */
44672     getUpdateManager : function(){
44673         return this.bodyEl.getUpdateManager();
44674     },
44675
44676     /**
44677      * Set a URL to be used to load the content for this TabPanelItem.
44678      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
44679      * @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)
44680      * @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)
44681      * @return {Roo.UpdateManager} The UpdateManager
44682      */
44683     setUrl : function(url, params, loadOnce){
44684         if(this.refreshDelegate){
44685             this.un('activate', this.refreshDelegate);
44686         }
44687         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44688         this.on("activate", this.refreshDelegate);
44689         return this.bodyEl.getUpdateManager();
44690     },
44691
44692     /** @private */
44693     _handleRefresh : function(url, params, loadOnce){
44694         if(!loadOnce || !this.loaded){
44695             var updater = this.bodyEl.getUpdateManager();
44696             updater.update(url, params, this._setLoaded.createDelegate(this));
44697         }
44698     },
44699
44700     /**
44701      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
44702      *   Will fail silently if the setUrl method has not been called.
44703      *   This does not activate the panel, just updates its content.
44704      */
44705     refresh : function(){
44706         if(this.refreshDelegate){
44707            this.loaded = false;
44708            this.refreshDelegate();
44709         }
44710     },
44711
44712     /** @private */
44713     _setLoaded : function(){
44714         this.loaded = true;
44715     },
44716
44717     /** @private */
44718     closeClick : function(e){
44719         var o = {};
44720         e.stopEvent();
44721         this.fireEvent("beforeclose", this, o);
44722         if(o.cancel !== true){
44723             this.tabPanel.removeTab(this.id);
44724         }
44725     },
44726     /**
44727      * The text displayed in the tooltip for the close icon.
44728      * @type String
44729      */
44730     closeText : "Close this tab"
44731 });
44732 /**
44733 *    This script refer to:
44734 *    Title: International Telephone Input
44735 *    Author: Jack O'Connor
44736 *    Code version:  v12.1.12
44737 *    Availability: https://github.com/jackocnr/intl-tel-input.git
44738 **/
44739
44740 Roo.bootstrap.form.PhoneInputData = function() {
44741     var d = [
44742       [
44743         "Afghanistan (‫افغانستان‬‎)",
44744         "af",
44745         "93"
44746       ],
44747       [
44748         "Albania (Shqipëri)",
44749         "al",
44750         "355"
44751       ],
44752       [
44753         "Algeria (‫الجزائر‬‎)",
44754         "dz",
44755         "213"
44756       ],
44757       [
44758         "American Samoa",
44759         "as",
44760         "1684"
44761       ],
44762       [
44763         "Andorra",
44764         "ad",
44765         "376"
44766       ],
44767       [
44768         "Angola",
44769         "ao",
44770         "244"
44771       ],
44772       [
44773         "Anguilla",
44774         "ai",
44775         "1264"
44776       ],
44777       [
44778         "Antigua and Barbuda",
44779         "ag",
44780         "1268"
44781       ],
44782       [
44783         "Argentina",
44784         "ar",
44785         "54"
44786       ],
44787       [
44788         "Armenia (Հայաստան)",
44789         "am",
44790         "374"
44791       ],
44792       [
44793         "Aruba",
44794         "aw",
44795         "297"
44796       ],
44797       [
44798         "Australia",
44799         "au",
44800         "61",
44801         0
44802       ],
44803       [
44804         "Austria (Österreich)",
44805         "at",
44806         "43"
44807       ],
44808       [
44809         "Azerbaijan (Azərbaycan)",
44810         "az",
44811         "994"
44812       ],
44813       [
44814         "Bahamas",
44815         "bs",
44816         "1242"
44817       ],
44818       [
44819         "Bahrain (‫البحرين‬‎)",
44820         "bh",
44821         "973"
44822       ],
44823       [
44824         "Bangladesh (বাংলাদেশ)",
44825         "bd",
44826         "880"
44827       ],
44828       [
44829         "Barbados",
44830         "bb",
44831         "1246"
44832       ],
44833       [
44834         "Belarus (Беларусь)",
44835         "by",
44836         "375"
44837       ],
44838       [
44839         "Belgium (België)",
44840         "be",
44841         "32"
44842       ],
44843       [
44844         "Belize",
44845         "bz",
44846         "501"
44847       ],
44848       [
44849         "Benin (Bénin)",
44850         "bj",
44851         "229"
44852       ],
44853       [
44854         "Bermuda",
44855         "bm",
44856         "1441"
44857       ],
44858       [
44859         "Bhutan (འབྲུག)",
44860         "bt",
44861         "975"
44862       ],
44863       [
44864         "Bolivia",
44865         "bo",
44866         "591"
44867       ],
44868       [
44869         "Bosnia and Herzegovina (Босна и Херцеговина)",
44870         "ba",
44871         "387"
44872       ],
44873       [
44874         "Botswana",
44875         "bw",
44876         "267"
44877       ],
44878       [
44879         "Brazil (Brasil)",
44880         "br",
44881         "55"
44882       ],
44883       [
44884         "British Indian Ocean Territory",
44885         "io",
44886         "246"
44887       ],
44888       [
44889         "British Virgin Islands",
44890         "vg",
44891         "1284"
44892       ],
44893       [
44894         "Brunei",
44895         "bn",
44896         "673"
44897       ],
44898       [
44899         "Bulgaria (България)",
44900         "bg",
44901         "359"
44902       ],
44903       [
44904         "Burkina Faso",
44905         "bf",
44906         "226"
44907       ],
44908       [
44909         "Burundi (Uburundi)",
44910         "bi",
44911         "257"
44912       ],
44913       [
44914         "Cambodia (កម្ពុជា)",
44915         "kh",
44916         "855"
44917       ],
44918       [
44919         "Cameroon (Cameroun)",
44920         "cm",
44921         "237"
44922       ],
44923       [
44924         "Canada",
44925         "ca",
44926         "1",
44927         1,
44928         ["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"]
44929       ],
44930       [
44931         "Cape Verde (Kabu Verdi)",
44932         "cv",
44933         "238"
44934       ],
44935       [
44936         "Caribbean Netherlands",
44937         "bq",
44938         "599",
44939         1
44940       ],
44941       [
44942         "Cayman Islands",
44943         "ky",
44944         "1345"
44945       ],
44946       [
44947         "Central African Republic (République centrafricaine)",
44948         "cf",
44949         "236"
44950       ],
44951       [
44952         "Chad (Tchad)",
44953         "td",
44954         "235"
44955       ],
44956       [
44957         "Chile",
44958         "cl",
44959         "56"
44960       ],
44961       [
44962         "China (中国)",
44963         "cn",
44964         "86"
44965       ],
44966       [
44967         "Christmas Island",
44968         "cx",
44969         "61",
44970         2
44971       ],
44972       [
44973         "Cocos (Keeling) Islands",
44974         "cc",
44975         "61",
44976         1
44977       ],
44978       [
44979         "Colombia",
44980         "co",
44981         "57"
44982       ],
44983       [
44984         "Comoros (‫جزر القمر‬‎)",
44985         "km",
44986         "269"
44987       ],
44988       [
44989         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
44990         "cd",
44991         "243"
44992       ],
44993       [
44994         "Congo (Republic) (Congo-Brazzaville)",
44995         "cg",
44996         "242"
44997       ],
44998       [
44999         "Cook Islands",
45000         "ck",
45001         "682"
45002       ],
45003       [
45004         "Costa Rica",
45005         "cr",
45006         "506"
45007       ],
45008       [
45009         "Côte d’Ivoire",
45010         "ci",
45011         "225"
45012       ],
45013       [
45014         "Croatia (Hrvatska)",
45015         "hr",
45016         "385"
45017       ],
45018       [
45019         "Cuba",
45020         "cu",
45021         "53"
45022       ],
45023       [
45024         "Curaçao",
45025         "cw",
45026         "599",
45027         0
45028       ],
45029       [
45030         "Cyprus (Κύπρος)",
45031         "cy",
45032         "357"
45033       ],
45034       [
45035         "Czech Republic (Česká republika)",
45036         "cz",
45037         "420"
45038       ],
45039       [
45040         "Denmark (Danmark)",
45041         "dk",
45042         "45"
45043       ],
45044       [
45045         "Djibouti",
45046         "dj",
45047         "253"
45048       ],
45049       [
45050         "Dominica",
45051         "dm",
45052         "1767"
45053       ],
45054       [
45055         "Dominican Republic (República Dominicana)",
45056         "do",
45057         "1",
45058         2,
45059         ["809", "829", "849"]
45060       ],
45061       [
45062         "Ecuador",
45063         "ec",
45064         "593"
45065       ],
45066       [
45067         "Egypt (‫مصر‬‎)",
45068         "eg",
45069         "20"
45070       ],
45071       [
45072         "El Salvador",
45073         "sv",
45074         "503"
45075       ],
45076       [
45077         "Equatorial Guinea (Guinea Ecuatorial)",
45078         "gq",
45079         "240"
45080       ],
45081       [
45082         "Eritrea",
45083         "er",
45084         "291"
45085       ],
45086       [
45087         "Estonia (Eesti)",
45088         "ee",
45089         "372"
45090       ],
45091       [
45092         "Ethiopia",
45093         "et",
45094         "251"
45095       ],
45096       [
45097         "Falkland Islands (Islas Malvinas)",
45098         "fk",
45099         "500"
45100       ],
45101       [
45102         "Faroe Islands (Føroyar)",
45103         "fo",
45104         "298"
45105       ],
45106       [
45107         "Fiji",
45108         "fj",
45109         "679"
45110       ],
45111       [
45112         "Finland (Suomi)",
45113         "fi",
45114         "358",
45115         0
45116       ],
45117       [
45118         "France",
45119         "fr",
45120         "33"
45121       ],
45122       [
45123         "French Guiana (Guyane française)",
45124         "gf",
45125         "594"
45126       ],
45127       [
45128         "French Polynesia (Polynésie française)",
45129         "pf",
45130         "689"
45131       ],
45132       [
45133         "Gabon",
45134         "ga",
45135         "241"
45136       ],
45137       [
45138         "Gambia",
45139         "gm",
45140         "220"
45141       ],
45142       [
45143         "Georgia (საქართველო)",
45144         "ge",
45145         "995"
45146       ],
45147       [
45148         "Germany (Deutschland)",
45149         "de",
45150         "49"
45151       ],
45152       [
45153         "Ghana (Gaana)",
45154         "gh",
45155         "233"
45156       ],
45157       [
45158         "Gibraltar",
45159         "gi",
45160         "350"
45161       ],
45162       [
45163         "Greece (Ελλάδα)",
45164         "gr",
45165         "30"
45166       ],
45167       [
45168         "Greenland (Kalaallit Nunaat)",
45169         "gl",
45170         "299"
45171       ],
45172       [
45173         "Grenada",
45174         "gd",
45175         "1473"
45176       ],
45177       [
45178         "Guadeloupe",
45179         "gp",
45180         "590",
45181         0
45182       ],
45183       [
45184         "Guam",
45185         "gu",
45186         "1671"
45187       ],
45188       [
45189         "Guatemala",
45190         "gt",
45191         "502"
45192       ],
45193       [
45194         "Guernsey",
45195         "gg",
45196         "44",
45197         1
45198       ],
45199       [
45200         "Guinea (Guinée)",
45201         "gn",
45202         "224"
45203       ],
45204       [
45205         "Guinea-Bissau (Guiné Bissau)",
45206         "gw",
45207         "245"
45208       ],
45209       [
45210         "Guyana",
45211         "gy",
45212         "592"
45213       ],
45214       [
45215         "Haiti",
45216         "ht",
45217         "509"
45218       ],
45219       [
45220         "Honduras",
45221         "hn",
45222         "504"
45223       ],
45224       [
45225         "Hong Kong (香港)",
45226         "hk",
45227         "852"
45228       ],
45229       [
45230         "Hungary (Magyarország)",
45231         "hu",
45232         "36"
45233       ],
45234       [
45235         "Iceland (Ísland)",
45236         "is",
45237         "354"
45238       ],
45239       [
45240         "India (भारत)",
45241         "in",
45242         "91"
45243       ],
45244       [
45245         "Indonesia",
45246         "id",
45247         "62"
45248       ],
45249       [
45250         "Iran (‫ایران‬‎)",
45251         "ir",
45252         "98"
45253       ],
45254       [
45255         "Iraq (‫العراق‬‎)",
45256         "iq",
45257         "964"
45258       ],
45259       [
45260         "Ireland",
45261         "ie",
45262         "353"
45263       ],
45264       [
45265         "Isle of Man",
45266         "im",
45267         "44",
45268         2
45269       ],
45270       [
45271         "Israel (‫ישראל‬‎)",
45272         "il",
45273         "972"
45274       ],
45275       [
45276         "Italy (Italia)",
45277         "it",
45278         "39",
45279         0
45280       ],
45281       [
45282         "Jamaica",
45283         "jm",
45284         "1876"
45285       ],
45286       [
45287         "Japan (日本)",
45288         "jp",
45289         "81"
45290       ],
45291       [
45292         "Jersey",
45293         "je",
45294         "44",
45295         3
45296       ],
45297       [
45298         "Jordan (‫الأردن‬‎)",
45299         "jo",
45300         "962"
45301       ],
45302       [
45303         "Kazakhstan (Казахстан)",
45304         "kz",
45305         "7",
45306         1
45307       ],
45308       [
45309         "Kenya",
45310         "ke",
45311         "254"
45312       ],
45313       [
45314         "Kiribati",
45315         "ki",
45316         "686"
45317       ],
45318       [
45319         "Kosovo",
45320         "xk",
45321         "383"
45322       ],
45323       [
45324         "Kuwait (‫الكويت‬‎)",
45325         "kw",
45326         "965"
45327       ],
45328       [
45329         "Kyrgyzstan (Кыргызстан)",
45330         "kg",
45331         "996"
45332       ],
45333       [
45334         "Laos (ລາວ)",
45335         "la",
45336         "856"
45337       ],
45338       [
45339         "Latvia (Latvija)",
45340         "lv",
45341         "371"
45342       ],
45343       [
45344         "Lebanon (‫لبنان‬‎)",
45345         "lb",
45346         "961"
45347       ],
45348       [
45349         "Lesotho",
45350         "ls",
45351         "266"
45352       ],
45353       [
45354         "Liberia",
45355         "lr",
45356         "231"
45357       ],
45358       [
45359         "Libya (‫ليبيا‬‎)",
45360         "ly",
45361         "218"
45362       ],
45363       [
45364         "Liechtenstein",
45365         "li",
45366         "423"
45367       ],
45368       [
45369         "Lithuania (Lietuva)",
45370         "lt",
45371         "370"
45372       ],
45373       [
45374         "Luxembourg",
45375         "lu",
45376         "352"
45377       ],
45378       [
45379         "Macau (澳門)",
45380         "mo",
45381         "853"
45382       ],
45383       [
45384         "Macedonia (FYROM) (Македонија)",
45385         "mk",
45386         "389"
45387       ],
45388       [
45389         "Madagascar (Madagasikara)",
45390         "mg",
45391         "261"
45392       ],
45393       [
45394         "Malawi",
45395         "mw",
45396         "265"
45397       ],
45398       [
45399         "Malaysia",
45400         "my",
45401         "60"
45402       ],
45403       [
45404         "Maldives",
45405         "mv",
45406         "960"
45407       ],
45408       [
45409         "Mali",
45410         "ml",
45411         "223"
45412       ],
45413       [
45414         "Malta",
45415         "mt",
45416         "356"
45417       ],
45418       [
45419         "Marshall Islands",
45420         "mh",
45421         "692"
45422       ],
45423       [
45424         "Martinique",
45425         "mq",
45426         "596"
45427       ],
45428       [
45429         "Mauritania (‫موريتانيا‬‎)",
45430         "mr",
45431         "222"
45432       ],
45433       [
45434         "Mauritius (Moris)",
45435         "mu",
45436         "230"
45437       ],
45438       [
45439         "Mayotte",
45440         "yt",
45441         "262",
45442         1
45443       ],
45444       [
45445         "Mexico (México)",
45446         "mx",
45447         "52"
45448       ],
45449       [
45450         "Micronesia",
45451         "fm",
45452         "691"
45453       ],
45454       [
45455         "Moldova (Republica Moldova)",
45456         "md",
45457         "373"
45458       ],
45459       [
45460         "Monaco",
45461         "mc",
45462         "377"
45463       ],
45464       [
45465         "Mongolia (Монгол)",
45466         "mn",
45467         "976"
45468       ],
45469       [
45470         "Montenegro (Crna Gora)",
45471         "me",
45472         "382"
45473       ],
45474       [
45475         "Montserrat",
45476         "ms",
45477         "1664"
45478       ],
45479       [
45480         "Morocco (‫المغرب‬‎)",
45481         "ma",
45482         "212",
45483         0
45484       ],
45485       [
45486         "Mozambique (Moçambique)",
45487         "mz",
45488         "258"
45489       ],
45490       [
45491         "Myanmar (Burma) (မြန်မာ)",
45492         "mm",
45493         "95"
45494       ],
45495       [
45496         "Namibia (Namibië)",
45497         "na",
45498         "264"
45499       ],
45500       [
45501         "Nauru",
45502         "nr",
45503         "674"
45504       ],
45505       [
45506         "Nepal (नेपाल)",
45507         "np",
45508         "977"
45509       ],
45510       [
45511         "Netherlands (Nederland)",
45512         "nl",
45513         "31"
45514       ],
45515       [
45516         "New Caledonia (Nouvelle-Calédonie)",
45517         "nc",
45518         "687"
45519       ],
45520       [
45521         "New Zealand",
45522         "nz",
45523         "64"
45524       ],
45525       [
45526         "Nicaragua",
45527         "ni",
45528         "505"
45529       ],
45530       [
45531         "Niger (Nijar)",
45532         "ne",
45533         "227"
45534       ],
45535       [
45536         "Nigeria",
45537         "ng",
45538         "234"
45539       ],
45540       [
45541         "Niue",
45542         "nu",
45543         "683"
45544       ],
45545       [
45546         "Norfolk Island",
45547         "nf",
45548         "672"
45549       ],
45550       [
45551         "North Korea (조선 민주주의 인민 공화국)",
45552         "kp",
45553         "850"
45554       ],
45555       [
45556         "Northern Mariana Islands",
45557         "mp",
45558         "1670"
45559       ],
45560       [
45561         "Norway (Norge)",
45562         "no",
45563         "47",
45564         0
45565       ],
45566       [
45567         "Oman (‫عُمان‬‎)",
45568         "om",
45569         "968"
45570       ],
45571       [
45572         "Pakistan (‫پاکستان‬‎)",
45573         "pk",
45574         "92"
45575       ],
45576       [
45577         "Palau",
45578         "pw",
45579         "680"
45580       ],
45581       [
45582         "Palestine (‫فلسطين‬‎)",
45583         "ps",
45584         "970"
45585       ],
45586       [
45587         "Panama (Panamá)",
45588         "pa",
45589         "507"
45590       ],
45591       [
45592         "Papua New Guinea",
45593         "pg",
45594         "675"
45595       ],
45596       [
45597         "Paraguay",
45598         "py",
45599         "595"
45600       ],
45601       [
45602         "Peru (Perú)",
45603         "pe",
45604         "51"
45605       ],
45606       [
45607         "Philippines",
45608         "ph",
45609         "63"
45610       ],
45611       [
45612         "Poland (Polska)",
45613         "pl",
45614         "48"
45615       ],
45616       [
45617         "Portugal",
45618         "pt",
45619         "351"
45620       ],
45621       [
45622         "Puerto Rico",
45623         "pr",
45624         "1",
45625         3,
45626         ["787", "939"]
45627       ],
45628       [
45629         "Qatar (‫قطر‬‎)",
45630         "qa",
45631         "974"
45632       ],
45633       [
45634         "Réunion (La Réunion)",
45635         "re",
45636         "262",
45637         0
45638       ],
45639       [
45640         "Romania (România)",
45641         "ro",
45642         "40"
45643       ],
45644       [
45645         "Russia (Россия)",
45646         "ru",
45647         "7",
45648         0
45649       ],
45650       [
45651         "Rwanda",
45652         "rw",
45653         "250"
45654       ],
45655       [
45656         "Saint Barthélemy",
45657         "bl",
45658         "590",
45659         1
45660       ],
45661       [
45662         "Saint Helena",
45663         "sh",
45664         "290"
45665       ],
45666       [
45667         "Saint Kitts and Nevis",
45668         "kn",
45669         "1869"
45670       ],
45671       [
45672         "Saint Lucia",
45673         "lc",
45674         "1758"
45675       ],
45676       [
45677         "Saint Martin (Saint-Martin (partie française))",
45678         "mf",
45679         "590",
45680         2
45681       ],
45682       [
45683         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
45684         "pm",
45685         "508"
45686       ],
45687       [
45688         "Saint Vincent and the Grenadines",
45689         "vc",
45690         "1784"
45691       ],
45692       [
45693         "Samoa",
45694         "ws",
45695         "685"
45696       ],
45697       [
45698         "San Marino",
45699         "sm",
45700         "378"
45701       ],
45702       [
45703         "São Tomé and Príncipe (São Tomé e Príncipe)",
45704         "st",
45705         "239"
45706       ],
45707       [
45708         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
45709         "sa",
45710         "966"
45711       ],
45712       [
45713         "Senegal (Sénégal)",
45714         "sn",
45715         "221"
45716       ],
45717       [
45718         "Serbia (Србија)",
45719         "rs",
45720         "381"
45721       ],
45722       [
45723         "Seychelles",
45724         "sc",
45725         "248"
45726       ],
45727       [
45728         "Sierra Leone",
45729         "sl",
45730         "232"
45731       ],
45732       [
45733         "Singapore",
45734         "sg",
45735         "65"
45736       ],
45737       [
45738         "Sint Maarten",
45739         "sx",
45740         "1721"
45741       ],
45742       [
45743         "Slovakia (Slovensko)",
45744         "sk",
45745         "421"
45746       ],
45747       [
45748         "Slovenia (Slovenija)",
45749         "si",
45750         "386"
45751       ],
45752       [
45753         "Solomon Islands",
45754         "sb",
45755         "677"
45756       ],
45757       [
45758         "Somalia (Soomaaliya)",
45759         "so",
45760         "252"
45761       ],
45762       [
45763         "South Africa",
45764         "za",
45765         "27"
45766       ],
45767       [
45768         "South Korea (대한민국)",
45769         "kr",
45770         "82"
45771       ],
45772       [
45773         "South Sudan (‫جنوب السودان‬‎)",
45774         "ss",
45775         "211"
45776       ],
45777       [
45778         "Spain (España)",
45779         "es",
45780         "34"
45781       ],
45782       [
45783         "Sri Lanka (ශ්‍රී ලංකාව)",
45784         "lk",
45785         "94"
45786       ],
45787       [
45788         "Sudan (‫السودان‬‎)",
45789         "sd",
45790         "249"
45791       ],
45792       [
45793         "Suriname",
45794         "sr",
45795         "597"
45796       ],
45797       [
45798         "Svalbard and Jan Mayen",
45799         "sj",
45800         "47",
45801         1
45802       ],
45803       [
45804         "Swaziland",
45805         "sz",
45806         "268"
45807       ],
45808       [
45809         "Sweden (Sverige)",
45810         "se",
45811         "46"
45812       ],
45813       [
45814         "Switzerland (Schweiz)",
45815         "ch",
45816         "41"
45817       ],
45818       [
45819         "Syria (‫سوريا‬‎)",
45820         "sy",
45821         "963"
45822       ],
45823       [
45824         "Taiwan (台灣)",
45825         "tw",
45826         "886"
45827       ],
45828       [
45829         "Tajikistan",
45830         "tj",
45831         "992"
45832       ],
45833       [
45834         "Tanzania",
45835         "tz",
45836         "255"
45837       ],
45838       [
45839         "Thailand (ไทย)",
45840         "th",
45841         "66"
45842       ],
45843       [
45844         "Timor-Leste",
45845         "tl",
45846         "670"
45847       ],
45848       [
45849         "Togo",
45850         "tg",
45851         "228"
45852       ],
45853       [
45854         "Tokelau",
45855         "tk",
45856         "690"
45857       ],
45858       [
45859         "Tonga",
45860         "to",
45861         "676"
45862       ],
45863       [
45864         "Trinidad and Tobago",
45865         "tt",
45866         "1868"
45867       ],
45868       [
45869         "Tunisia (‫تونس‬‎)",
45870         "tn",
45871         "216"
45872       ],
45873       [
45874         "Turkey (Türkiye)",
45875         "tr",
45876         "90"
45877       ],
45878       [
45879         "Turkmenistan",
45880         "tm",
45881         "993"
45882       ],
45883       [
45884         "Turks and Caicos Islands",
45885         "tc",
45886         "1649"
45887       ],
45888       [
45889         "Tuvalu",
45890         "tv",
45891         "688"
45892       ],
45893       [
45894         "U.S. Virgin Islands",
45895         "vi",
45896         "1340"
45897       ],
45898       [
45899         "Uganda",
45900         "ug",
45901         "256"
45902       ],
45903       [
45904         "Ukraine (Україна)",
45905         "ua",
45906         "380"
45907       ],
45908       [
45909         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
45910         "ae",
45911         "971"
45912       ],
45913       [
45914         "United Kingdom",
45915         "gb",
45916         "44",
45917         0
45918       ],
45919       [
45920         "United States",
45921         "us",
45922         "1",
45923         0
45924       ],
45925       [
45926         "Uruguay",
45927         "uy",
45928         "598"
45929       ],
45930       [
45931         "Uzbekistan (Oʻzbekiston)",
45932         "uz",
45933         "998"
45934       ],
45935       [
45936         "Vanuatu",
45937         "vu",
45938         "678"
45939       ],
45940       [
45941         "Vatican City (Città del Vaticano)",
45942         "va",
45943         "39",
45944         1
45945       ],
45946       [
45947         "Venezuela",
45948         "ve",
45949         "58"
45950       ],
45951       [
45952         "Vietnam (Việt Nam)",
45953         "vn",
45954         "84"
45955       ],
45956       [
45957         "Wallis and Futuna (Wallis-et-Futuna)",
45958         "wf",
45959         "681"
45960       ],
45961       [
45962         "Western Sahara (‫الصحراء الغربية‬‎)",
45963         "eh",
45964         "212",
45965         1
45966       ],
45967       [
45968         "Yemen (‫اليمن‬‎)",
45969         "ye",
45970         "967"
45971       ],
45972       [
45973         "Zambia",
45974         "zm",
45975         "260"
45976       ],
45977       [
45978         "Zimbabwe",
45979         "zw",
45980         "263"
45981       ],
45982       [
45983         "Åland Islands",
45984         "ax",
45985         "358",
45986         1
45987       ]
45988   ];
45989   
45990   return d;
45991 }/**
45992 *    This script refer to:
45993 *    Title: International Telephone Input
45994 *    Author: Jack O'Connor
45995 *    Code version:  v12.1.12
45996 *    Availability: https://github.com/jackocnr/intl-tel-input.git
45997 **/
45998
45999 /**
46000  * @class Roo.bootstrap.form.PhoneInput
46001  * @extends Roo.bootstrap.form.TriggerField
46002  * An input with International dial-code selection
46003  
46004  * @cfg {String} defaultDialCode default '+852'
46005  * @cfg {Array} preferedCountries default []
46006   
46007  * @constructor
46008  * Create a new PhoneInput.
46009  * @param {Object} config Configuration options
46010  */
46011
46012 Roo.bootstrap.form.PhoneInput = function(config) {
46013     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
46014 };
46015
46016 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
46017         /**
46018         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
46019         */
46020         listWidth: undefined,
46021         
46022         selectedClass: 'active',
46023         
46024         invalidClass : "has-warning",
46025         
46026         validClass: 'has-success',
46027         
46028         allowed: '0123456789',
46029         
46030         max_length: 15,
46031         
46032         /**
46033          * @cfg {String} defaultDialCode The default dial code when initializing the input
46034          */
46035         defaultDialCode: '+852',
46036         
46037         /**
46038          * @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
46039          */
46040         preferedCountries: false,
46041         
46042         getAutoCreate : function()
46043         {
46044             var data = Roo.bootstrap.form.PhoneInputData();
46045             var align = this.labelAlign || this.parentLabelAlign();
46046             var id = Roo.id();
46047             
46048             this.allCountries = [];
46049             this.dialCodeMapping = [];
46050             
46051             for (var i = 0; i < data.length; i++) {
46052               var c = data[i];
46053               this.allCountries[i] = {
46054                 name: c[0],
46055                 iso2: c[1],
46056                 dialCode: c[2],
46057                 priority: c[3] || 0,
46058                 areaCodes: c[4] || null
46059               };
46060               this.dialCodeMapping[c[2]] = {
46061                   name: c[0],
46062                   iso2: c[1],
46063                   priority: c[3] || 0,
46064                   areaCodes: c[4] || null
46065               };
46066             }
46067             
46068             var cfg = {
46069                 cls: 'form-group',
46070                 cn: []
46071             };
46072             
46073             var input =  {
46074                 tag: 'input',
46075                 id : id,
46076                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
46077                 maxlength: this.max_length,
46078                 cls : 'form-control tel-input',
46079                 autocomplete: 'new-password'
46080             };
46081             
46082             var hiddenInput = {
46083                 tag: 'input',
46084                 type: 'hidden',
46085                 cls: 'hidden-tel-input'
46086             };
46087             
46088             if (this.name) {
46089                 hiddenInput.name = this.name;
46090             }
46091             
46092             if (this.disabled) {
46093                 input.disabled = true;
46094             }
46095             
46096             var flag_container = {
46097                 tag: 'div',
46098                 cls: 'flag-box',
46099                 cn: [
46100                     {
46101                         tag: 'div',
46102                         cls: 'flag'
46103                     },
46104                     {
46105                         tag: 'div',
46106                         cls: 'caret'
46107                     }
46108                 ]
46109             };
46110             
46111             var box = {
46112                 tag: 'div',
46113                 cls: this.hasFeedback ? 'has-feedback' : '',
46114                 cn: [
46115                     hiddenInput,
46116                     input,
46117                     {
46118                         tag: 'input',
46119                         cls: 'dial-code-holder',
46120                         disabled: true
46121                     }
46122                 ]
46123             };
46124             
46125             var container = {
46126                 cls: 'roo-select2-container input-group',
46127                 cn: [
46128                     flag_container,
46129                     box
46130                 ]
46131             };
46132             
46133             if (this.fieldLabel.length) {
46134                 var indicator = {
46135                     tag: 'i',
46136                     tooltip: 'This field is required'
46137                 };
46138                 
46139                 var label = {
46140                     tag: 'label',
46141                     'for':  id,
46142                     cls: 'control-label',
46143                     cn: []
46144                 };
46145                 
46146                 var label_text = {
46147                     tag: 'span',
46148                     html: this.fieldLabel
46149                 };
46150                 
46151                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46152                 label.cn = [
46153                     indicator,
46154                     label_text
46155                 ];
46156                 
46157                 if(this.indicatorpos == 'right') {
46158                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46159                     label.cn = [
46160                         label_text,
46161                         indicator
46162                     ];
46163                 }
46164                 
46165                 if(align == 'left') {
46166                     container = {
46167                         tag: 'div',
46168                         cn: [
46169                             container
46170                         ]
46171                     };
46172                     
46173                     if(this.labelWidth > 12){
46174                         label.style = "width: " + this.labelWidth + 'px';
46175                     }
46176                     if(this.labelWidth < 13 && this.labelmd == 0){
46177                         this.labelmd = this.labelWidth;
46178                     }
46179                     if(this.labellg > 0){
46180                         label.cls += ' col-lg-' + this.labellg;
46181                         input.cls += ' col-lg-' + (12 - this.labellg);
46182                     }
46183                     if(this.labelmd > 0){
46184                         label.cls += ' col-md-' + this.labelmd;
46185                         container.cls += ' col-md-' + (12 - this.labelmd);
46186                     }
46187                     if(this.labelsm > 0){
46188                         label.cls += ' col-sm-' + this.labelsm;
46189                         container.cls += ' col-sm-' + (12 - this.labelsm);
46190                     }
46191                     if(this.labelxs > 0){
46192                         label.cls += ' col-xs-' + this.labelxs;
46193                         container.cls += ' col-xs-' + (12 - this.labelxs);
46194                     }
46195                 }
46196             }
46197             
46198             cfg.cn = [
46199                 label,
46200                 container
46201             ];
46202             
46203             var settings = this;
46204             
46205             ['xs','sm','md','lg'].map(function(size){
46206                 if (settings[size]) {
46207                     cfg.cls += ' col-' + size + '-' + settings[size];
46208                 }
46209             });
46210             
46211             this.store = new Roo.data.Store({
46212                 proxy : new Roo.data.MemoryProxy({}),
46213                 reader : new Roo.data.JsonReader({
46214                     fields : [
46215                         {
46216                             'name' : 'name',
46217                             'type' : 'string'
46218                         },
46219                         {
46220                             'name' : 'iso2',
46221                             'type' : 'string'
46222                         },
46223                         {
46224                             'name' : 'dialCode',
46225                             'type' : 'string'
46226                         },
46227                         {
46228                             'name' : 'priority',
46229                             'type' : 'string'
46230                         },
46231                         {
46232                             'name' : 'areaCodes',
46233                             'type' : 'string'
46234                         }
46235                     ]
46236                 })
46237             });
46238             
46239             if(!this.preferedCountries) {
46240                 this.preferedCountries = [
46241                     'hk',
46242                     'gb',
46243                     'us'
46244                 ];
46245             }
46246             
46247             var p = this.preferedCountries.reverse();
46248             
46249             if(p) {
46250                 for (var i = 0; i < p.length; i++) {
46251                     for (var j = 0; j < this.allCountries.length; j++) {
46252                         if(this.allCountries[j].iso2 == p[i]) {
46253                             var t = this.allCountries[j];
46254                             this.allCountries.splice(j,1);
46255                             this.allCountries.unshift(t);
46256                         }
46257                     } 
46258                 }
46259             }
46260             
46261             this.store.proxy.data = {
46262                 success: true,
46263                 data: this.allCountries
46264             };
46265             
46266             return cfg;
46267         },
46268         
46269         initEvents : function()
46270         {
46271             this.createList();
46272             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
46273             
46274             this.indicator = this.indicatorEl();
46275             this.flag = this.flagEl();
46276             this.dialCodeHolder = this.dialCodeHolderEl();
46277             
46278             this.trigger = this.el.select('div.flag-box',true).first();
46279             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
46280             
46281             var _this = this;
46282             
46283             (function(){
46284                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
46285                 _this.list.setWidth(lw);
46286             }).defer(100);
46287             
46288             this.list.on('mouseover', this.onViewOver, this);
46289             this.list.on('mousemove', this.onViewMove, this);
46290             this.inputEl().on("keyup", this.onKeyUp, this);
46291             this.inputEl().on("keypress", this.onKeyPress, this);
46292             
46293             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
46294
46295             this.view = new Roo.View(this.list, this.tpl, {
46296                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
46297             });
46298             
46299             this.view.on('click', this.onViewClick, this);
46300             this.setValue(this.defaultDialCode);
46301         },
46302         
46303         onTriggerClick : function(e)
46304         {
46305             Roo.log('trigger click');
46306             if(this.disabled){
46307                 return;
46308             }
46309             
46310             if(this.isExpanded()){
46311                 this.collapse();
46312                 this.hasFocus = false;
46313             }else {
46314                 this.store.load({});
46315                 this.hasFocus = true;
46316                 this.expand();
46317             }
46318         },
46319         
46320         isExpanded : function()
46321         {
46322             return this.list.isVisible();
46323         },
46324         
46325         collapse : function()
46326         {
46327             if(!this.isExpanded()){
46328                 return;
46329             }
46330             this.list.hide();
46331             Roo.get(document).un('mousedown', this.collapseIf, this);
46332             Roo.get(document).un('mousewheel', this.collapseIf, this);
46333             this.fireEvent('collapse', this);
46334             this.validate();
46335         },
46336         
46337         expand : function()
46338         {
46339             Roo.log('expand');
46340
46341             if(this.isExpanded() || !this.hasFocus){
46342                 return;
46343             }
46344             
46345             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
46346             this.list.setWidth(lw);
46347             
46348             this.list.show();
46349             this.restrictHeight();
46350             
46351             Roo.get(document).on('mousedown', this.collapseIf, this);
46352             Roo.get(document).on('mousewheel', this.collapseIf, this);
46353             
46354             this.fireEvent('expand', this);
46355         },
46356         
46357         restrictHeight : function()
46358         {
46359             this.list.alignTo(this.inputEl(), this.listAlign);
46360             this.list.alignTo(this.inputEl(), this.listAlign);
46361         },
46362         
46363         onViewOver : function(e, t)
46364         {
46365             if(this.inKeyMode){
46366                 return;
46367             }
46368             var item = this.view.findItemFromChild(t);
46369             
46370             if(item){
46371                 var index = this.view.indexOf(item);
46372                 this.select(index, false);
46373             }
46374         },
46375
46376         // private
46377         onViewClick : function(view, doFocus, el, e)
46378         {
46379             var index = this.view.getSelectedIndexes()[0];
46380             
46381             var r = this.store.getAt(index);
46382             
46383             if(r){
46384                 this.onSelect(r, index);
46385             }
46386             if(doFocus !== false && !this.blockFocus){
46387                 this.inputEl().focus();
46388             }
46389         },
46390         
46391         onViewMove : function(e, t)
46392         {
46393             this.inKeyMode = false;
46394         },
46395         
46396         select : function(index, scrollIntoView)
46397         {
46398             this.selectedIndex = index;
46399             this.view.select(index);
46400             if(scrollIntoView !== false){
46401                 var el = this.view.getNode(index);
46402                 if(el){
46403                     this.list.scrollChildIntoView(el, false);
46404                 }
46405             }
46406         },
46407         
46408         createList : function()
46409         {
46410             this.list = Roo.get(document.body).createChild({
46411                 tag: 'ul',
46412                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
46413                 style: 'display:none'
46414             });
46415             
46416             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
46417         },
46418         
46419         collapseIf : function(e)
46420         {
46421             var in_combo  = e.within(this.el);
46422             var in_list =  e.within(this.list);
46423             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
46424             
46425             if (in_combo || in_list || is_list) {
46426                 return;
46427             }
46428             this.collapse();
46429         },
46430         
46431         onSelect : function(record, index)
46432         {
46433             if(this.fireEvent('beforeselect', this, record, index) !== false){
46434                 
46435                 this.setFlagClass(record.data.iso2);
46436                 this.setDialCode(record.data.dialCode);
46437                 this.hasFocus = false;
46438                 this.collapse();
46439                 this.fireEvent('select', this, record, index);
46440             }
46441         },
46442         
46443         flagEl : function()
46444         {
46445             var flag = this.el.select('div.flag',true).first();
46446             if(!flag){
46447                 return false;
46448             }
46449             return flag;
46450         },
46451         
46452         dialCodeHolderEl : function()
46453         {
46454             var d = this.el.select('input.dial-code-holder',true).first();
46455             if(!d){
46456                 return false;
46457             }
46458             return d;
46459         },
46460         
46461         setDialCode : function(v)
46462         {
46463             this.dialCodeHolder.dom.value = '+'+v;
46464         },
46465         
46466         setFlagClass : function(n)
46467         {
46468             this.flag.dom.className = 'flag '+n;
46469         },
46470         
46471         getValue : function()
46472         {
46473             var v = this.inputEl().getValue();
46474             if(this.dialCodeHolder) {
46475                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
46476             }
46477             return v;
46478         },
46479         
46480         setValue : function(v)
46481         {
46482             var d = this.getDialCode(v);
46483             
46484             //invalid dial code
46485             if(v.length == 0 || !d || d.length == 0) {
46486                 if(this.rendered){
46487                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
46488                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
46489                 }
46490                 return;
46491             }
46492             
46493             //valid dial code
46494             this.setFlagClass(this.dialCodeMapping[d].iso2);
46495             this.setDialCode(d);
46496             this.inputEl().dom.value = v.replace('+'+d,'');
46497             this.hiddenEl().dom.value = this.getValue();
46498             
46499             this.validate();
46500         },
46501         
46502         getDialCode : function(v)
46503         {
46504             v = v ||  '';
46505             
46506             if (v.length == 0) {
46507                 return this.dialCodeHolder.dom.value;
46508             }
46509             
46510             var dialCode = "";
46511             if (v.charAt(0) != "+") {
46512                 return false;
46513             }
46514             var numericChars = "";
46515             for (var i = 1; i < v.length; i++) {
46516               var c = v.charAt(i);
46517               if (!isNaN(c)) {
46518                 numericChars += c;
46519                 if (this.dialCodeMapping[numericChars]) {
46520                   dialCode = v.substr(1, i);
46521                 }
46522                 if (numericChars.length == 4) {
46523                   break;
46524                 }
46525               }
46526             }
46527             return dialCode;
46528         },
46529         
46530         reset : function()
46531         {
46532             this.setValue(this.defaultDialCode);
46533             this.validate();
46534         },
46535         
46536         hiddenEl : function()
46537         {
46538             return this.el.select('input.hidden-tel-input',true).first();
46539         },
46540         
46541         // after setting val
46542         onKeyUp : function(e){
46543             this.setValue(this.getValue());
46544         },
46545         
46546         onKeyPress : function(e){
46547             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
46548                 e.stopEvent();
46549             }
46550         }
46551         
46552 });
46553 /**
46554  * @class Roo.bootstrap.form.MoneyField
46555  * @extends Roo.bootstrap.form.ComboBox
46556  * Bootstrap MoneyField class
46557  * 
46558  * @constructor
46559  * Create a new MoneyField.
46560  * @param {Object} config Configuration options
46561  */
46562
46563 Roo.bootstrap.form.MoneyField = function(config) {
46564     
46565     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
46566     
46567 };
46568
46569 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
46570     
46571     /**
46572      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
46573      */
46574     allowDecimals : true,
46575     /**
46576      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
46577      */
46578     decimalSeparator : ".",
46579     /**
46580      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
46581      */
46582     decimalPrecision : 0,
46583     /**
46584      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
46585      */
46586     allowNegative : true,
46587     /**
46588      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
46589      */
46590     allowZero: true,
46591     /**
46592      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
46593      */
46594     minValue : Number.NEGATIVE_INFINITY,
46595     /**
46596      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
46597      */
46598     maxValue : Number.MAX_VALUE,
46599     /**
46600      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
46601      */
46602     minText : "The minimum value for this field is {0}",
46603     /**
46604      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
46605      */
46606     maxText : "The maximum value for this field is {0}",
46607     /**
46608      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
46609      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
46610      */
46611     nanText : "{0} is not a valid number",
46612     /**
46613      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
46614      */
46615     castInt : true,
46616     /**
46617      * @cfg {String} defaults currency of the MoneyField
46618      * value should be in lkey
46619      */
46620     defaultCurrency : false,
46621     /**
46622      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
46623      */
46624     thousandsDelimiter : false,
46625     /**
46626      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
46627      */
46628     max_length: false,
46629     
46630     inputlg : 9,
46631     inputmd : 9,
46632     inputsm : 9,
46633     inputxs : 6,
46634      /**
46635      * @cfg {Roo.data.Store} store  Store to lookup currency??
46636      */
46637     store : false,
46638     
46639     getAutoCreate : function()
46640     {
46641         var align = this.labelAlign || this.parentLabelAlign();
46642         
46643         var id = Roo.id();
46644
46645         var cfg = {
46646             cls: 'form-group',
46647             cn: []
46648         };
46649
46650         var input =  {
46651             tag: 'input',
46652             id : id,
46653             cls : 'form-control roo-money-amount-input',
46654             autocomplete: 'new-password'
46655         };
46656         
46657         var hiddenInput = {
46658             tag: 'input',
46659             type: 'hidden',
46660             id: Roo.id(),
46661             cls: 'hidden-number-input'
46662         };
46663         
46664         if(this.max_length) {
46665             input.maxlength = this.max_length; 
46666         }
46667         
46668         if (this.name) {
46669             hiddenInput.name = this.name;
46670         }
46671
46672         if (this.disabled) {
46673             input.disabled = true;
46674         }
46675
46676         var clg = 12 - this.inputlg;
46677         var cmd = 12 - this.inputmd;
46678         var csm = 12 - this.inputsm;
46679         var cxs = 12 - this.inputxs;
46680         
46681         var container = {
46682             tag : 'div',
46683             cls : 'row roo-money-field',
46684             cn : [
46685                 {
46686                     tag : 'div',
46687                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
46688                     cn : [
46689                         {
46690                             tag : 'div',
46691                             cls: 'roo-select2-container input-group',
46692                             cn: [
46693                                 {
46694                                     tag : 'input',
46695                                     cls : 'form-control roo-money-currency-input',
46696                                     autocomplete: 'new-password',
46697                                     readOnly : 1,
46698                                     name : this.currencyName
46699                                 },
46700                                 {
46701                                     tag :'span',
46702                                     cls : 'input-group-addon',
46703                                     cn : [
46704                                         {
46705                                             tag: 'span',
46706                                             cls: 'caret'
46707                                         }
46708                                     ]
46709                                 }
46710                             ]
46711                         }
46712                     ]
46713                 },
46714                 {
46715                     tag : 'div',
46716                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
46717                     cn : [
46718                         {
46719                             tag: 'div',
46720                             cls: this.hasFeedback ? 'has-feedback' : '',
46721                             cn: [
46722                                 input
46723                             ]
46724                         }
46725                     ]
46726                 }
46727             ]
46728             
46729         };
46730         
46731         if (this.fieldLabel.length) {
46732             var indicator = {
46733                 tag: 'i',
46734                 tooltip: 'This field is required'
46735             };
46736
46737             var label = {
46738                 tag: 'label',
46739                 'for':  id,
46740                 cls: 'control-label',
46741                 cn: []
46742             };
46743
46744             var label_text = {
46745                 tag: 'span',
46746                 html: this.fieldLabel
46747             };
46748
46749             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46750             label.cn = [
46751                 indicator,
46752                 label_text
46753             ];
46754
46755             if(this.indicatorpos == 'right') {
46756                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46757                 label.cn = [
46758                     label_text,
46759                     indicator
46760                 ];
46761             }
46762
46763             if(align == 'left') {
46764                 container = {
46765                     tag: 'div',
46766                     cn: [
46767                         container
46768                     ]
46769                 };
46770
46771                 if(this.labelWidth > 12){
46772                     label.style = "width: " + this.labelWidth + 'px';
46773                 }
46774                 if(this.labelWidth < 13 && this.labelmd == 0){
46775                     this.labelmd = this.labelWidth;
46776                 }
46777                 if(this.labellg > 0){
46778                     label.cls += ' col-lg-' + this.labellg;
46779                     input.cls += ' col-lg-' + (12 - this.labellg);
46780                 }
46781                 if(this.labelmd > 0){
46782                     label.cls += ' col-md-' + this.labelmd;
46783                     container.cls += ' col-md-' + (12 - this.labelmd);
46784                 }
46785                 if(this.labelsm > 0){
46786                     label.cls += ' col-sm-' + this.labelsm;
46787                     container.cls += ' col-sm-' + (12 - this.labelsm);
46788                 }
46789                 if(this.labelxs > 0){
46790                     label.cls += ' col-xs-' + this.labelxs;
46791                     container.cls += ' col-xs-' + (12 - this.labelxs);
46792                 }
46793             }
46794         }
46795
46796         cfg.cn = [
46797             label,
46798             container,
46799             hiddenInput
46800         ];
46801         
46802         var settings = this;
46803
46804         ['xs','sm','md','lg'].map(function(size){
46805             if (settings[size]) {
46806                 cfg.cls += ' col-' + size + '-' + settings[size];
46807             }
46808         });
46809         
46810         return cfg;
46811     },
46812     
46813     initEvents : function()
46814     {
46815         this.indicator = this.indicatorEl();
46816         
46817         this.initCurrencyEvent();
46818         
46819         this.initNumberEvent();
46820     },
46821     
46822     initCurrencyEvent : function()
46823     {
46824         if (!this.store) {
46825             throw "can not find store for combo";
46826         }
46827         
46828         this.store = Roo.factory(this.store, Roo.data);
46829         this.store.parent = this;
46830         
46831         this.createList();
46832         
46833         this.triggerEl = this.el.select('.input-group-addon', true).first();
46834         
46835         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
46836         
46837         var _this = this;
46838         
46839         (function(){
46840             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
46841             _this.list.setWidth(lw);
46842         }).defer(100);
46843         
46844         this.list.on('mouseover', this.onViewOver, this);
46845         this.list.on('mousemove', this.onViewMove, this);
46846         this.list.on('scroll', this.onViewScroll, this);
46847         
46848         if(!this.tpl){
46849             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
46850         }
46851         
46852         this.view = new Roo.View(this.list, this.tpl, {
46853             singleSelect:true, store: this.store, selectedClass: this.selectedClass
46854         });
46855         
46856         this.view.on('click', this.onViewClick, this);
46857         
46858         this.store.on('beforeload', this.onBeforeLoad, this);
46859         this.store.on('load', this.onLoad, this);
46860         this.store.on('loadexception', this.onLoadException, this);
46861         
46862         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
46863             "up" : function(e){
46864                 this.inKeyMode = true;
46865                 this.selectPrev();
46866             },
46867
46868             "down" : function(e){
46869                 if(!this.isExpanded()){
46870                     this.onTriggerClick();
46871                 }else{
46872                     this.inKeyMode = true;
46873                     this.selectNext();
46874                 }
46875             },
46876
46877             "enter" : function(e){
46878                 this.collapse();
46879                 
46880                 if(this.fireEvent("specialkey", this, e)){
46881                     this.onViewClick(false);
46882                 }
46883                 
46884                 return true;
46885             },
46886
46887             "esc" : function(e){
46888                 this.collapse();
46889             },
46890
46891             "tab" : function(e){
46892                 this.collapse();
46893                 
46894                 if(this.fireEvent("specialkey", this, e)){
46895                     this.onViewClick(false);
46896                 }
46897                 
46898                 return true;
46899             },
46900
46901             scope : this,
46902
46903             doRelay : function(foo, bar, hname){
46904                 if(hname == 'down' || this.scope.isExpanded()){
46905                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
46906                 }
46907                 return true;
46908             },
46909
46910             forceKeyDown: true
46911         });
46912         
46913         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
46914         
46915     },
46916     
46917     initNumberEvent : function(e)
46918     {
46919         this.inputEl().on("keydown" , this.fireKey,  this);
46920         this.inputEl().on("focus", this.onFocus,  this);
46921         this.inputEl().on("blur", this.onBlur,  this);
46922         
46923         this.inputEl().relayEvent('keyup', this);
46924         
46925         if(this.indicator){
46926             this.indicator.addClass('invisible');
46927         }
46928  
46929         this.originalValue = this.getValue();
46930         
46931         if(this.validationEvent == 'keyup'){
46932             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
46933             this.inputEl().on('keyup', this.filterValidation, this);
46934         }
46935         else if(this.validationEvent !== false){
46936             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
46937         }
46938         
46939         if(this.selectOnFocus){
46940             this.on("focus", this.preFocus, this);
46941             
46942         }
46943         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
46944             this.inputEl().on("keypress", this.filterKeys, this);
46945         } else {
46946             this.inputEl().relayEvent('keypress', this);
46947         }
46948         
46949         var allowed = "0123456789";
46950         
46951         if(this.allowDecimals){
46952             allowed += this.decimalSeparator;
46953         }
46954         
46955         if(this.allowNegative){
46956             allowed += "-";
46957         }
46958         
46959         if(this.thousandsDelimiter) {
46960             allowed += ",";
46961         }
46962         
46963         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
46964         
46965         var keyPress = function(e){
46966             
46967             var k = e.getKey();
46968             
46969             var c = e.getCharCode();
46970             
46971             if(
46972                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
46973                     allowed.indexOf(String.fromCharCode(c)) === -1
46974             ){
46975                 e.stopEvent();
46976                 return;
46977             }
46978             
46979             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
46980                 return;
46981             }
46982             
46983             if(allowed.indexOf(String.fromCharCode(c)) === -1){
46984                 e.stopEvent();
46985             }
46986         };
46987         
46988         this.inputEl().on("keypress", keyPress, this);
46989         
46990     },
46991     
46992     onTriggerClick : function(e)
46993     {   
46994         if(this.disabled){
46995             return;
46996         }
46997         
46998         this.page = 0;
46999         this.loadNext = false;
47000         
47001         if(this.isExpanded()){
47002             this.collapse();
47003             return;
47004         }
47005         
47006         this.hasFocus = true;
47007         
47008         if(this.triggerAction == 'all') {
47009             this.doQuery(this.allQuery, true);
47010             return;
47011         }
47012         
47013         this.doQuery(this.getRawValue());
47014     },
47015     
47016     getCurrency : function()
47017     {   
47018         var v = this.currencyEl().getValue();
47019         
47020         return v;
47021     },
47022     
47023     restrictHeight : function()
47024     {
47025         this.list.alignTo(this.currencyEl(), this.listAlign);
47026         this.list.alignTo(this.currencyEl(), this.listAlign);
47027     },
47028     
47029     onViewClick : function(view, doFocus, el, e)
47030     {
47031         var index = this.view.getSelectedIndexes()[0];
47032         
47033         var r = this.store.getAt(index);
47034         
47035         if(r){
47036             this.onSelect(r, index);
47037         }
47038     },
47039     
47040     onSelect : function(record, index){
47041         
47042         if(this.fireEvent('beforeselect', this, record, index) !== false){
47043         
47044             this.setFromCurrencyData(index > -1 ? record.data : false);
47045             
47046             this.collapse();
47047             
47048             this.fireEvent('select', this, record, index);
47049         }
47050     },
47051     
47052     setFromCurrencyData : function(o)
47053     {
47054         var currency = '';
47055         
47056         this.lastCurrency = o;
47057         
47058         if (this.currencyField) {
47059             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
47060         } else {
47061             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
47062         }
47063         
47064         this.lastSelectionText = currency;
47065         
47066         //setting default currency
47067         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
47068             this.setCurrency(this.defaultCurrency);
47069             return;
47070         }
47071         
47072         this.setCurrency(currency);
47073     },
47074     
47075     setFromData : function(o)
47076     {
47077         var c = {};
47078         
47079         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
47080         
47081         this.setFromCurrencyData(c);
47082         
47083         var value = '';
47084         
47085         if (this.name) {
47086             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
47087         } else {
47088             Roo.log('no value set for '+ (this.name ? this.name : this.id));
47089         }
47090         
47091         this.setValue(value);
47092         
47093     },
47094     
47095     setCurrency : function(v)
47096     {   
47097         this.currencyValue = v;
47098         
47099         if(this.rendered){
47100             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
47101             this.validate();
47102         }
47103     },
47104     
47105     setValue : function(v)
47106     {
47107         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
47108         
47109         this.value = v;
47110         
47111         if(this.rendered){
47112             
47113             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
47114             
47115             this.inputEl().dom.value = (v == '') ? '' :
47116                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
47117             
47118             if(!this.allowZero && v === '0') {
47119                 this.hiddenEl().dom.value = '';
47120                 this.inputEl().dom.value = '';
47121             }
47122             
47123             this.validate();
47124         }
47125     },
47126     
47127     getRawValue : function()
47128     {
47129         var v = this.inputEl().getValue();
47130         
47131         return v;
47132     },
47133     
47134     getValue : function()
47135     {
47136         return this.fixPrecision(this.parseValue(this.getRawValue()));
47137     },
47138     
47139     parseValue : function(value)
47140     {
47141         if(this.thousandsDelimiter) {
47142             value += "";
47143             r = new RegExp(",", "g");
47144             value = value.replace(r, "");
47145         }
47146         
47147         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
47148         return isNaN(value) ? '' : value;
47149         
47150     },
47151     
47152     fixPrecision : function(value)
47153     {
47154         if(this.thousandsDelimiter) {
47155             value += "";
47156             r = new RegExp(",", "g");
47157             value = value.replace(r, "");
47158         }
47159         
47160         var nan = isNaN(value);
47161         
47162         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
47163             return nan ? '' : value;
47164         }
47165         return parseFloat(value).toFixed(this.decimalPrecision);
47166     },
47167     
47168     decimalPrecisionFcn : function(v)
47169     {
47170         return Math.floor(v);
47171     },
47172     
47173     validateValue : function(value)
47174     {
47175         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
47176             return false;
47177         }
47178         
47179         var num = this.parseValue(value);
47180         
47181         if(isNaN(num)){
47182             this.markInvalid(String.format(this.nanText, value));
47183             return false;
47184         }
47185         
47186         if(num < this.minValue){
47187             this.markInvalid(String.format(this.minText, this.minValue));
47188             return false;
47189         }
47190         
47191         if(num > this.maxValue){
47192             this.markInvalid(String.format(this.maxText, this.maxValue));
47193             return false;
47194         }
47195         
47196         return true;
47197     },
47198     
47199     validate : function()
47200     {
47201         if(this.disabled || this.allowBlank){
47202             this.markValid();
47203             return true;
47204         }
47205         
47206         var currency = this.getCurrency();
47207         
47208         if(this.validateValue(this.getRawValue()) && currency.length){
47209             this.markValid();
47210             return true;
47211         }
47212         
47213         this.markInvalid();
47214         return false;
47215     },
47216     
47217     getName: function()
47218     {
47219         return this.name;
47220     },
47221     
47222     beforeBlur : function()
47223     {
47224         if(!this.castInt){
47225             return;
47226         }
47227         
47228         var v = this.parseValue(this.getRawValue());
47229         
47230         if(v || v == 0){
47231             this.setValue(v);
47232         }
47233     },
47234     
47235     onBlur : function()
47236     {
47237         this.beforeBlur();
47238         
47239         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
47240             //this.el.removeClass(this.focusClass);
47241         }
47242         
47243         this.hasFocus = false;
47244         
47245         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
47246             this.validate();
47247         }
47248         
47249         var v = this.getValue();
47250         
47251         if(String(v) !== String(this.startValue)){
47252             this.fireEvent('change', this, v, this.startValue);
47253         }
47254         
47255         this.fireEvent("blur", this);
47256     },
47257     
47258     inputEl : function()
47259     {
47260         return this.el.select('.roo-money-amount-input', true).first();
47261     },
47262     
47263     currencyEl : function()
47264     {
47265         return this.el.select('.roo-money-currency-input', true).first();
47266     },
47267     
47268     hiddenEl : function()
47269     {
47270         return this.el.select('input.hidden-number-input',true).first();
47271     }
47272     
47273 });/**
47274  * @class Roo.bootstrap.BezierSignature
47275  * @extends Roo.bootstrap.Component
47276  * Bootstrap BezierSignature class
47277  * This script refer to:
47278  *    Title: Signature Pad
47279  *    Author: szimek
47280  *    Availability: https://github.com/szimek/signature_pad
47281  *
47282  * @constructor
47283  * Create a new BezierSignature
47284  * @param {Object} config The config object
47285  */
47286
47287 Roo.bootstrap.BezierSignature = function(config){
47288     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
47289     this.addEvents({
47290         "resize" : true
47291     });
47292 };
47293
47294 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
47295 {
47296      
47297     curve_data: [],
47298     
47299     is_empty: true,
47300     
47301     mouse_btn_down: true,
47302     
47303     /**
47304      * @cfg {int} canvas height
47305      */
47306     canvas_height: '200px',
47307     
47308     /**
47309      * @cfg {float|function} Radius of a single dot.
47310      */ 
47311     dot_size: false,
47312     
47313     /**
47314      * @cfg {float} Minimum width of a line. Defaults to 0.5.
47315      */
47316     min_width: 0.5,
47317     
47318     /**
47319      * @cfg {float} Maximum width of a line. Defaults to 2.5.
47320      */
47321     max_width: 2.5,
47322     
47323     /**
47324      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
47325      */
47326     throttle: 16,
47327     
47328     /**
47329      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
47330      */
47331     min_distance: 5,
47332     
47333     /**
47334      * @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.
47335      */
47336     bg_color: 'rgba(0, 0, 0, 0)',
47337     
47338     /**
47339      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
47340      */
47341     dot_color: 'black',
47342     
47343     /**
47344      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
47345      */ 
47346     velocity_filter_weight: 0.7,
47347     
47348     /**
47349      * @cfg {function} Callback when stroke begin. 
47350      */
47351     onBegin: false,
47352     
47353     /**
47354      * @cfg {function} Callback when stroke end.
47355      */
47356     onEnd: false,
47357     
47358     getAutoCreate : function()
47359     {
47360         var cls = 'roo-signature column';
47361         
47362         if(this.cls){
47363             cls += ' ' + this.cls;
47364         }
47365         
47366         var col_sizes = [
47367             'lg',
47368             'md',
47369             'sm',
47370             'xs'
47371         ];
47372         
47373         for(var i = 0; i < col_sizes.length; i++) {
47374             if(this[col_sizes[i]]) {
47375                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
47376             }
47377         }
47378         
47379         var cfg = {
47380             tag: 'div',
47381             cls: cls,
47382             cn: [
47383                 {
47384                     tag: 'div',
47385                     cls: 'roo-signature-body',
47386                     cn: [
47387                         {
47388                             tag: 'canvas',
47389                             cls: 'roo-signature-body-canvas',
47390                             height: this.canvas_height,
47391                             width: this.canvas_width
47392                         }
47393                     ]
47394                 },
47395                 {
47396                     tag: 'input',
47397                     type: 'file',
47398                     style: 'display: none'
47399                 }
47400             ]
47401         };
47402         
47403         return cfg;
47404     },
47405     
47406     initEvents: function() 
47407     {
47408         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
47409         
47410         var canvas = this.canvasEl();
47411         
47412         // mouse && touch event swapping...
47413         canvas.dom.style.touchAction = 'none';
47414         canvas.dom.style.msTouchAction = 'none';
47415         
47416         this.mouse_btn_down = false;
47417         canvas.on('mousedown', this._handleMouseDown, this);
47418         canvas.on('mousemove', this._handleMouseMove, this);
47419         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
47420         
47421         if (window.PointerEvent) {
47422             canvas.on('pointerdown', this._handleMouseDown, this);
47423             canvas.on('pointermove', this._handleMouseMove, this);
47424             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
47425         }
47426         
47427         if ('ontouchstart' in window) {
47428             canvas.on('touchstart', this._handleTouchStart, this);
47429             canvas.on('touchmove', this._handleTouchMove, this);
47430             canvas.on('touchend', this._handleTouchEnd, this);
47431         }
47432         
47433         Roo.EventManager.onWindowResize(this.resize, this, true);
47434         
47435         // file input event
47436         this.fileEl().on('change', this.uploadImage, this);
47437         
47438         this.clear();
47439         
47440         this.resize();
47441     },
47442     
47443     resize: function(){
47444         
47445         var canvas = this.canvasEl().dom;
47446         var ctx = this.canvasElCtx();
47447         var img_data = false;
47448         
47449         if(canvas.width > 0) {
47450             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
47451         }
47452         // setting canvas width will clean img data
47453         canvas.width = 0;
47454         
47455         var style = window.getComputedStyle ? 
47456             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
47457             
47458         var padding_left = parseInt(style.paddingLeft) || 0;
47459         var padding_right = parseInt(style.paddingRight) || 0;
47460         
47461         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
47462         
47463         if(img_data) {
47464             ctx.putImageData(img_data, 0, 0);
47465         }
47466     },
47467     
47468     _handleMouseDown: function(e)
47469     {
47470         if (e.browserEvent.which === 1) {
47471             this.mouse_btn_down = true;
47472             this.strokeBegin(e);
47473         }
47474     },
47475     
47476     _handleMouseMove: function (e)
47477     {
47478         if (this.mouse_btn_down) {
47479             this.strokeMoveUpdate(e);
47480         }
47481     },
47482     
47483     _handleMouseUp: function (e)
47484     {
47485         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
47486             this.mouse_btn_down = false;
47487             this.strokeEnd(e);
47488         }
47489     },
47490     
47491     _handleTouchStart: function (e) {
47492         
47493         e.preventDefault();
47494         if (e.browserEvent.targetTouches.length === 1) {
47495             // var touch = e.browserEvent.changedTouches[0];
47496             // this.strokeBegin(touch);
47497             
47498              this.strokeBegin(e); // assume e catching the correct xy...
47499         }
47500     },
47501     
47502     _handleTouchMove: function (e) {
47503         e.preventDefault();
47504         // var touch = event.targetTouches[0];
47505         // _this._strokeMoveUpdate(touch);
47506         this.strokeMoveUpdate(e);
47507     },
47508     
47509     _handleTouchEnd: function (e) {
47510         var wasCanvasTouched = e.target === this.canvasEl().dom;
47511         if (wasCanvasTouched) {
47512             e.preventDefault();
47513             // var touch = event.changedTouches[0];
47514             // _this._strokeEnd(touch);
47515             this.strokeEnd(e);
47516         }
47517     },
47518     
47519     reset: function () {
47520         this._lastPoints = [];
47521         this._lastVelocity = 0;
47522         this._lastWidth = (this.min_width + this.max_width) / 2;
47523         this.canvasElCtx().fillStyle = this.dot_color;
47524     },
47525     
47526     strokeMoveUpdate: function(e)
47527     {
47528         this.strokeUpdate(e);
47529         
47530         if (this.throttle) {
47531             this.throttleStroke(this.strokeUpdate, this.throttle);
47532         }
47533         else {
47534             this.strokeUpdate(e);
47535         }
47536     },
47537     
47538     strokeBegin: function(e)
47539     {
47540         var newPointGroup = {
47541             color: this.dot_color,
47542             points: []
47543         };
47544         
47545         if (typeof this.onBegin === 'function') {
47546             this.onBegin(e);
47547         }
47548         
47549         this.curve_data.push(newPointGroup);
47550         this.reset();
47551         this.strokeUpdate(e);
47552     },
47553     
47554     strokeUpdate: function(e)
47555     {
47556         var rect = this.canvasEl().dom.getBoundingClientRect();
47557         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
47558         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
47559         var lastPoints = lastPointGroup.points;
47560         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
47561         var isLastPointTooClose = lastPoint
47562             ? point.distanceTo(lastPoint) <= this.min_distance
47563             : false;
47564         var color = lastPointGroup.color;
47565         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
47566             var curve = this.addPoint(point);
47567             if (!lastPoint) {
47568                 this.drawDot({color: color, point: point});
47569             }
47570             else if (curve) {
47571                 this.drawCurve({color: color, curve: curve});
47572             }
47573             lastPoints.push({
47574                 time: point.time,
47575                 x: point.x,
47576                 y: point.y
47577             });
47578         }
47579     },
47580     
47581     strokeEnd: function(e)
47582     {
47583         this.strokeUpdate(e);
47584         if (typeof this.onEnd === 'function') {
47585             this.onEnd(e);
47586         }
47587     },
47588     
47589     addPoint:  function (point) {
47590         var _lastPoints = this._lastPoints;
47591         _lastPoints.push(point);
47592         if (_lastPoints.length > 2) {
47593             if (_lastPoints.length === 3) {
47594                 _lastPoints.unshift(_lastPoints[0]);
47595             }
47596             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
47597             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
47598             _lastPoints.shift();
47599             return curve;
47600         }
47601         return null;
47602     },
47603     
47604     calculateCurveWidths: function (startPoint, endPoint) {
47605         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
47606             (1 - this.velocity_filter_weight) * this._lastVelocity;
47607
47608         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
47609         var widths = {
47610             end: newWidth,
47611             start: this._lastWidth
47612         };
47613         
47614         this._lastVelocity = velocity;
47615         this._lastWidth = newWidth;
47616         return widths;
47617     },
47618     
47619     drawDot: function (_a) {
47620         var color = _a.color, point = _a.point;
47621         var ctx = this.canvasElCtx();
47622         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
47623         ctx.beginPath();
47624         this.drawCurveSegment(point.x, point.y, width);
47625         ctx.closePath();
47626         ctx.fillStyle = color;
47627         ctx.fill();
47628     },
47629     
47630     drawCurve: function (_a) {
47631         var color = _a.color, curve = _a.curve;
47632         var ctx = this.canvasElCtx();
47633         var widthDelta = curve.endWidth - curve.startWidth;
47634         var drawSteps = Math.floor(curve.length()) * 2;
47635         ctx.beginPath();
47636         ctx.fillStyle = color;
47637         for (var i = 0; i < drawSteps; i += 1) {
47638         var t = i / drawSteps;
47639         var tt = t * t;
47640         var ttt = tt * t;
47641         var u = 1 - t;
47642         var uu = u * u;
47643         var uuu = uu * u;
47644         var x = uuu * curve.startPoint.x;
47645         x += 3 * uu * t * curve.control1.x;
47646         x += 3 * u * tt * curve.control2.x;
47647         x += ttt * curve.endPoint.x;
47648         var y = uuu * curve.startPoint.y;
47649         y += 3 * uu * t * curve.control1.y;
47650         y += 3 * u * tt * curve.control2.y;
47651         y += ttt * curve.endPoint.y;
47652         var width = curve.startWidth + ttt * widthDelta;
47653         this.drawCurveSegment(x, y, width);
47654         }
47655         ctx.closePath();
47656         ctx.fill();
47657     },
47658     
47659     drawCurveSegment: function (x, y, width) {
47660         var ctx = this.canvasElCtx();
47661         ctx.moveTo(x, y);
47662         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
47663         this.is_empty = false;
47664     },
47665     
47666     clear: function()
47667     {
47668         var ctx = this.canvasElCtx();
47669         var canvas = this.canvasEl().dom;
47670         ctx.fillStyle = this.bg_color;
47671         ctx.clearRect(0, 0, canvas.width, canvas.height);
47672         ctx.fillRect(0, 0, canvas.width, canvas.height);
47673         this.curve_data = [];
47674         this.reset();
47675         this.is_empty = true;
47676     },
47677     
47678     fileEl: function()
47679     {
47680         return  this.el.select('input',true).first();
47681     },
47682     
47683     canvasEl: function()
47684     {
47685         return this.el.select('canvas',true).first();
47686     },
47687     
47688     canvasElCtx: function()
47689     {
47690         return this.el.select('canvas',true).first().dom.getContext('2d');
47691     },
47692     
47693     getImage: function(type)
47694     {
47695         if(this.is_empty) {
47696             return false;
47697         }
47698         
47699         // encryption ?
47700         return this.canvasEl().dom.toDataURL('image/'+type, 1);
47701     },
47702     
47703     drawFromImage: function(img_src)
47704     {
47705         var img = new Image();
47706         
47707         img.onload = function(){
47708             this.canvasElCtx().drawImage(img, 0, 0);
47709         }.bind(this);
47710         
47711         img.src = img_src;
47712         
47713         this.is_empty = false;
47714     },
47715     
47716     selectImage: function()
47717     {
47718         this.fileEl().dom.click();
47719     },
47720     
47721     uploadImage: function(e)
47722     {
47723         var reader = new FileReader();
47724         
47725         reader.onload = function(e){
47726             var img = new Image();
47727             img.onload = function(){
47728                 this.reset();
47729                 this.canvasElCtx().drawImage(img, 0, 0);
47730             }.bind(this);
47731             img.src = e.target.result;
47732         }.bind(this);
47733         
47734         reader.readAsDataURL(e.target.files[0]);
47735     },
47736     
47737     // Bezier Point Constructor
47738     Point: (function () {
47739         function Point(x, y, time) {
47740             this.x = x;
47741             this.y = y;
47742             this.time = time || Date.now();
47743         }
47744         Point.prototype.distanceTo = function (start) {
47745             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
47746         };
47747         Point.prototype.equals = function (other) {
47748             return this.x === other.x && this.y === other.y && this.time === other.time;
47749         };
47750         Point.prototype.velocityFrom = function (start) {
47751             return this.time !== start.time
47752             ? this.distanceTo(start) / (this.time - start.time)
47753             : 0;
47754         };
47755         return Point;
47756     }()),
47757     
47758     
47759     // Bezier Constructor
47760     Bezier: (function () {
47761         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
47762             this.startPoint = startPoint;
47763             this.control2 = control2;
47764             this.control1 = control1;
47765             this.endPoint = endPoint;
47766             this.startWidth = startWidth;
47767             this.endWidth = endWidth;
47768         }
47769         Bezier.fromPoints = function (points, widths, scope) {
47770             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
47771             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
47772             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
47773         };
47774         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
47775             var dx1 = s1.x - s2.x;
47776             var dy1 = s1.y - s2.y;
47777             var dx2 = s2.x - s3.x;
47778             var dy2 = s2.y - s3.y;
47779             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
47780             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
47781             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
47782             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
47783             var dxm = m1.x - m2.x;
47784             var dym = m1.y - m2.y;
47785             var k = l2 / (l1 + l2);
47786             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
47787             var tx = s2.x - cm.x;
47788             var ty = s2.y - cm.y;
47789             return {
47790                 c1: new scope.Point(m1.x + tx, m1.y + ty),
47791                 c2: new scope.Point(m2.x + tx, m2.y + ty)
47792             };
47793         };
47794         Bezier.prototype.length = function () {
47795             var steps = 10;
47796             var length = 0;
47797             var px;
47798             var py;
47799             for (var i = 0; i <= steps; i += 1) {
47800                 var t = i / steps;
47801                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
47802                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
47803                 if (i > 0) {
47804                     var xdiff = cx - px;
47805                     var ydiff = cy - py;
47806                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
47807                 }
47808                 px = cx;
47809                 py = cy;
47810             }
47811             return length;
47812         };
47813         Bezier.prototype.point = function (t, start, c1, c2, end) {
47814             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
47815             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
47816             + (3.0 * c2 * (1.0 - t) * t * t)
47817             + (end * t * t * t);
47818         };
47819         return Bezier;
47820     }()),
47821     
47822     throttleStroke: function(fn, wait) {
47823       if (wait === void 0) { wait = 250; }
47824       var previous = 0;
47825       var timeout = null;
47826       var result;
47827       var storedContext;
47828       var storedArgs;
47829       var later = function () {
47830           previous = Date.now();
47831           timeout = null;
47832           result = fn.apply(storedContext, storedArgs);
47833           if (!timeout) {
47834               storedContext = null;
47835               storedArgs = [];
47836           }
47837       };
47838       return function wrapper() {
47839           var args = [];
47840           for (var _i = 0; _i < arguments.length; _i++) {
47841               args[_i] = arguments[_i];
47842           }
47843           var now = Date.now();
47844           var remaining = wait - (now - previous);
47845           storedContext = this;
47846           storedArgs = args;
47847           if (remaining <= 0 || remaining > wait) {
47848               if (timeout) {
47849                   clearTimeout(timeout);
47850                   timeout = null;
47851               }
47852               previous = now;
47853               result = fn.apply(storedContext, storedArgs);
47854               if (!timeout) {
47855                   storedContext = null;
47856                   storedArgs = [];
47857               }
47858           }
47859           else if (!timeout) {
47860               timeout = window.setTimeout(later, remaining);
47861           }
47862           return result;
47863       };
47864   }
47865   
47866 });
47867
47868  
47869
47870  // old names for form elements
47871 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
47872 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
47873 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
47874 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
47875 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
47876 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
47877 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
47878 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
47879 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
47880 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
47881 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
47882 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
47883 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
47884 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
47885 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
47886 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
47887 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
47888 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
47889 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
47890 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
47891 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
47892 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
47893 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
47894 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
47895 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
47896 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
47897
47898 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
47899 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
47900
47901 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
47902 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
47903
47904 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
47905 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
47906 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
47907 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
47908