roojs-core.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         console.log("BOOTSTRAP UPLOADCROPBOX GETAUTOCREATE");
33469         var cfg = {
33470             tag : 'div',
33471             cls : 'roo-upload-cropbox',
33472             cn : [
33473                 {
33474                     tag : 'input',
33475                     cls : 'roo-upload-cropbox-selector',
33476                     type : 'file'
33477                 },
33478                 {
33479                     tag : 'div',
33480                     cls : 'roo-upload-cropbox-body',
33481                     style : 'cursor:pointer',
33482                     cn : [
33483                         {
33484                             tag : 'div',
33485                             cls : 'roo-upload-cropbox-preview'
33486                         },
33487                         {
33488                             tag : 'div',
33489                             cls : 'roo-upload-cropbox-thumb'
33490                         },
33491                         {
33492                             tag : 'div',
33493                             cls : 'roo-upload-cropbox-empty-notify',
33494                             html : this.emptyText
33495                         },
33496                         {
33497                             tag : 'div',
33498                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
33499                             html : this.rotateNotify
33500                         }
33501                     ]
33502                 },
33503                 {
33504                     tag : 'div',
33505                     cls : 'roo-upload-cropbox-footer',
33506                     cn : {
33507                         tag : 'div',
33508                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
33509                         cn : []
33510                     }
33511                 }
33512             ]
33513         };
33514         
33515         return cfg;
33516     },
33517     
33518     onRender : function(ct, position)
33519     {
33520         console.log("BOOTSTRAP UPLOADCROPBOX ONRENDER");
33521         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
33522         
33523         if (this.buttons.length) {
33524             
33525             Roo.each(this.buttons, function(bb) {
33526                 
33527                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
33528                 
33529                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
33530                 
33531             }, this);
33532         }
33533         
33534         if(this.loadMask){
33535             this.maskEl = this.el;
33536         }
33537     },
33538     
33539     initEvents : function()
33540     {
33541         this.urlAPI = (window.createObjectURL && window) || 
33542                                 (window.URL && URL.revokeObjectURL && URL) || 
33543                                 (window.webkitURL && webkitURL);
33544                         
33545         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
33546         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33547         
33548         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
33549         this.selectorEl.hide();
33550         
33551         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
33552         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33553         
33554         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
33555         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33556         this.thumbEl.hide();
33557         
33558         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
33559         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33560         
33561         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
33562         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33563         this.errorEl.hide();
33564         
33565         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
33566         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33567         this.footerEl.hide();
33568         
33569         this.setThumbBoxSize();
33570         
33571         this.bind();
33572         
33573         this.resize();
33574         
33575         this.fireEvent('initial', this);
33576     },
33577
33578     bind : function()
33579     {
33580         var _this = this;
33581         
33582         window.addEventListener("resize", function() { _this.resize(); } );
33583         
33584         this.bodyEl.on('click', this.beforeSelectFile, this);
33585         
33586         if(Roo.isTouch){
33587             this.bodyEl.on('touchstart', this.onTouchStart, this);
33588             this.bodyEl.on('touchmove', this.onTouchMove, this);
33589             this.bodyEl.on('touchend', this.onTouchEnd, this);
33590         }
33591         
33592         if(!Roo.isTouch){
33593             this.bodyEl.on('mousedown', this.onMouseDown, this);
33594             this.bodyEl.on('mousemove', this.onMouseMove, this);
33595             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
33596             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
33597             Roo.get(document).on('mouseup', this.onMouseUp, this);
33598         }
33599         
33600         this.selectorEl.on('change', this.onFileSelected, this);
33601     },
33602     
33603     reset : function()
33604     {    
33605         this.scale = 0;
33606         this.baseScale = 1;
33607         this.rotate = 0;
33608         this.baseRotate = 1;
33609         this.dragable = false;
33610         this.pinching = false;
33611         this.mouseX = 0;
33612         this.mouseY = 0;
33613         this.cropData = false;
33614         this.notifyEl.dom.innerHTML = this.emptyText;
33615         
33616         this.selectorEl.dom.value = '';
33617         
33618     },
33619     
33620     resize : function()
33621     {
33622         if(this.fireEvent('resize', this) != false){
33623             this.setThumbBoxPosition();
33624             this.setCanvasPosition();
33625         }
33626     },
33627     
33628     onFooterButtonClick : function(e, el, o, type)
33629     {
33630         switch (type) {
33631             case 'rotate-left' :
33632                 this.onRotateLeft(e);
33633                 break;
33634             case 'rotate-right' :
33635                 this.onRotateRight(e);
33636                 break;
33637             case 'picture' :
33638                 this.beforeSelectFile(e);
33639                 break;
33640             case 'trash' :
33641                 this.trash(e);
33642                 break;
33643             case 'crop' :
33644                 this.crop(e);
33645                 break;
33646             case 'download' :
33647                 this.download(e);
33648                 break;
33649             default :
33650                 break;
33651         }
33652         
33653         this.fireEvent('footerbuttonclick', this, type);
33654     },
33655     
33656     beforeSelectFile : function(e)
33657     {
33658         e.preventDefault();
33659         
33660         if(this.fireEvent('beforeselectfile', this) != false){
33661             this.selectorEl.dom.click();
33662         }
33663     },
33664     
33665     onFileSelected : function(e)
33666     {
33667         e.preventDefault();
33668         
33669         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
33670             return;
33671         }
33672         
33673         var file = this.selectorEl.dom.files[0];
33674         
33675         if(this.fireEvent('inspect', this, file) != false){
33676             this.prepare(file);
33677         }
33678         
33679     },
33680     
33681     trash : function(e)
33682     {
33683         this.fireEvent('trash', this);
33684     },
33685     
33686     download : function(e)
33687     {
33688         this.fireEvent('download', this);
33689     },
33690     
33691     loadCanvas : function(src)
33692     {   
33693         if(this.fireEvent('beforeloadcanvas', this, src) != false){
33694             
33695             this.reset();
33696             
33697             this.imageEl = document.createElement('img');
33698             
33699             var _this = this;
33700             
33701             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
33702             
33703             this.imageEl.src = src;
33704         }
33705     },
33706     
33707     onLoadCanvas : function()
33708     {   
33709         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
33710         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
33711         
33712         this.bodyEl.un('click', this.beforeSelectFile, this);
33713         
33714         this.notifyEl.hide();
33715         this.thumbEl.show();
33716         this.footerEl.show();
33717         
33718         this.baseRotateLevel();
33719         
33720         if(this.isDocument){
33721             this.setThumbBoxSize();
33722         }
33723         
33724         this.setThumbBoxPosition();
33725         
33726         this.baseScaleLevel();
33727         
33728         this.draw();
33729         
33730         this.resize();
33731         
33732         this.canvasLoaded = true;
33733         
33734         if(this.loadMask){
33735             this.maskEl.unmask();
33736         }
33737         
33738     },
33739     
33740     setCanvasPosition : function()
33741     {   
33742         if(!this.canvasEl){
33743             return;
33744         }
33745         
33746         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
33747         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
33748         
33749         this.previewEl.setLeft(pw);
33750         this.previewEl.setTop(ph);
33751         
33752     },
33753     
33754     onMouseDown : function(e)
33755     {   
33756         e.stopEvent();
33757         
33758         this.dragable = true;
33759         this.pinching = false;
33760         
33761         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
33762             this.dragable = false;
33763             return;
33764         }
33765         
33766         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33767         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33768         
33769     },
33770     
33771     onMouseMove : function(e)
33772     {   
33773         e.stopEvent();
33774         
33775         if(!this.canvasLoaded){
33776             return;
33777         }
33778         
33779         if (!this.dragable){
33780             return;
33781         }
33782         
33783         var minX = Math.ceil(this.thumbEl.getLeft(true));
33784         var minY = Math.ceil(this.thumbEl.getTop(true));
33785         
33786         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
33787         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
33788         
33789         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33790         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33791         
33792         x = x - this.mouseX;
33793         y = y - this.mouseY;
33794         
33795         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
33796         var bgY = Math.ceil(y + this.previewEl.getTop(true));
33797         
33798         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
33799         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
33800         
33801         this.previewEl.setLeft(bgX);
33802         this.previewEl.setTop(bgY);
33803         
33804         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33805         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33806     },
33807     
33808     onMouseUp : function(e)
33809     {   
33810         e.stopEvent();
33811         
33812         this.dragable = false;
33813     },
33814     
33815     onMouseWheel : function(e)
33816     {   
33817         e.stopEvent();
33818         
33819         this.startScale = this.scale;
33820         
33821         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
33822         
33823         if(!this.zoomable()){
33824             this.scale = this.startScale;
33825             return;
33826         }
33827         
33828         this.draw();
33829         
33830         return;
33831     },
33832     
33833     zoomable : function()
33834     {
33835         var minScale = this.thumbEl.getWidth() / this.minWidth;
33836         
33837         if(this.minWidth < this.minHeight){
33838             minScale = this.thumbEl.getHeight() / this.minHeight;
33839         }
33840         
33841         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
33842         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
33843         
33844         if(
33845                 this.isDocument &&
33846                 (this.rotate == 0 || this.rotate == 180) && 
33847                 (
33848                     width > this.imageEl.OriginWidth || 
33849                     height > this.imageEl.OriginHeight ||
33850                     (width < this.minWidth && height < this.minHeight)
33851                 )
33852         ){
33853             return false;
33854         }
33855         
33856         if(
33857                 this.isDocument &&
33858                 (this.rotate == 90 || this.rotate == 270) && 
33859                 (
33860                     width > this.imageEl.OriginWidth || 
33861                     height > this.imageEl.OriginHeight ||
33862                     (width < this.minHeight && height < this.minWidth)
33863                 )
33864         ){
33865             return false;
33866         }
33867         
33868         if(
33869                 !this.isDocument &&
33870                 (this.rotate == 0 || this.rotate == 180) && 
33871                 (
33872                     width < this.minWidth || 
33873                     width > this.imageEl.OriginWidth || 
33874                     height < this.minHeight || 
33875                     height > this.imageEl.OriginHeight
33876                 )
33877         ){
33878             return false;
33879         }
33880         
33881         if(
33882                 !this.isDocument &&
33883                 (this.rotate == 90 || this.rotate == 270) && 
33884                 (
33885                     width < this.minHeight || 
33886                     width > this.imageEl.OriginWidth || 
33887                     height < this.minWidth || 
33888                     height > this.imageEl.OriginHeight
33889                 )
33890         ){
33891             return false;
33892         }
33893         
33894         return true;
33895         
33896     },
33897     
33898     onRotateLeft : function(e)
33899     {   
33900         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
33901             
33902             var minScale = this.thumbEl.getWidth() / this.minWidth;
33903             
33904             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
33905             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
33906             
33907             this.startScale = this.scale;
33908             
33909             while (this.getScaleLevel() < minScale){
33910             
33911                 this.scale = this.scale + 1;
33912                 
33913                 if(!this.zoomable()){
33914                     break;
33915                 }
33916                 
33917                 if(
33918                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
33919                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
33920                 ){
33921                     continue;
33922                 }
33923                 
33924                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
33925
33926                 this.draw();
33927                 
33928                 return;
33929             }
33930             
33931             this.scale = this.startScale;
33932             
33933             this.onRotateFail();
33934             
33935             return false;
33936         }
33937         
33938         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
33939
33940         if(this.isDocument){
33941             this.setThumbBoxSize();
33942             this.setThumbBoxPosition();
33943             this.setCanvasPosition();
33944         }
33945         
33946         this.draw();
33947         
33948         this.fireEvent('rotate', this, 'left');
33949         
33950     },
33951     
33952     onRotateRight : function(e)
33953     {
33954         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
33955             
33956             var minScale = this.thumbEl.getWidth() / this.minWidth;
33957         
33958             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
33959             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
33960             
33961             this.startScale = this.scale;
33962             
33963             while (this.getScaleLevel() < minScale){
33964             
33965                 this.scale = this.scale + 1;
33966                 
33967                 if(!this.zoomable()){
33968                     break;
33969                 }
33970                 
33971                 if(
33972                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
33973                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
33974                 ){
33975                     continue;
33976                 }
33977                 
33978                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
33979
33980                 this.draw();
33981                 
33982                 return;
33983             }
33984             
33985             this.scale = this.startScale;
33986             
33987             this.onRotateFail();
33988             
33989             return false;
33990         }
33991         
33992         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
33993
33994         if(this.isDocument){
33995             this.setThumbBoxSize();
33996             this.setThumbBoxPosition();
33997             this.setCanvasPosition();
33998         }
33999         
34000         this.draw();
34001         
34002         this.fireEvent('rotate', this, 'right');
34003     },
34004     
34005     onRotateFail : function()
34006     {
34007         this.errorEl.show(true);
34008         
34009         var _this = this;
34010         
34011         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
34012     },
34013     
34014     draw : function()
34015     {
34016         this.previewEl.dom.innerHTML = '';
34017         
34018         var canvasEl = document.createElement("canvas");
34019         
34020         var contextEl = canvasEl.getContext("2d");
34021         
34022         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34023         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34024         var center = this.imageEl.OriginWidth / 2;
34025         
34026         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
34027             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34028             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34029             center = this.imageEl.OriginHeight / 2;
34030         }
34031         
34032         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
34033         
34034         contextEl.translate(center, center);
34035         contextEl.rotate(this.rotate * Math.PI / 180);
34036
34037         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34038         
34039         this.canvasEl = document.createElement("canvas");
34040         
34041         this.contextEl = this.canvasEl.getContext("2d");
34042         
34043         switch (this.rotate) {
34044             case 0 :
34045                 
34046                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34047                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34048                 
34049                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34050                 
34051                 break;
34052             case 90 : 
34053                 
34054                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34055                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34056                 
34057                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34058                     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);
34059                     break;
34060                 }
34061                 
34062                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34063                 
34064                 break;
34065             case 180 :
34066                 
34067                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34068                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34069                 
34070                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34071                     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);
34072                     break;
34073                 }
34074                 
34075                 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);
34076                 
34077                 break;
34078             case 270 :
34079                 
34080                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34081                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34082         
34083                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34084                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34085                     break;
34086                 }
34087                 
34088                 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);
34089                 
34090                 break;
34091             default : 
34092                 break;
34093         }
34094         
34095         this.previewEl.appendChild(this.canvasEl);
34096         
34097         this.setCanvasPosition();
34098     },
34099     
34100     crop : function()
34101     {
34102         if(!this.canvasLoaded){
34103             return;
34104         }
34105         
34106         var imageCanvas = document.createElement("canvas");
34107         
34108         var imageContext = imageCanvas.getContext("2d");
34109         
34110         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34111         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34112         
34113         var center = imageCanvas.width / 2;
34114         
34115         imageContext.translate(center, center);
34116         
34117         imageContext.rotate(this.rotate * Math.PI / 180);
34118         
34119         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34120         
34121         var canvas = document.createElement("canvas");
34122         
34123         var context = canvas.getContext("2d");
34124                 
34125         canvas.width = this.minWidth;
34126         canvas.height = this.minHeight;
34127
34128         switch (this.rotate) {
34129             case 0 :
34130                 
34131                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34132                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34133                 
34134                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34135                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34136                 
34137                 var targetWidth = this.minWidth - 2 * x;
34138                 var targetHeight = this.minHeight - 2 * y;
34139                 
34140                 var scale = 1;
34141                 
34142                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34143                     scale = targetWidth / width;
34144                 }
34145                 
34146                 if(x > 0 && y == 0){
34147                     scale = targetHeight / height;
34148                 }
34149                 
34150                 if(x > 0 && y > 0){
34151                     scale = targetWidth / width;
34152                     
34153                     if(width < height){
34154                         scale = targetHeight / height;
34155                     }
34156                 }
34157                 
34158                 context.scale(scale, scale);
34159                 
34160                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34161                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34162
34163                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34164                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34165
34166                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34167                 
34168                 break;
34169             case 90 : 
34170                 
34171                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34172                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34173                 
34174                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34175                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34176                 
34177                 var targetWidth = this.minWidth - 2 * x;
34178                 var targetHeight = this.minHeight - 2 * y;
34179                 
34180                 var scale = 1;
34181                 
34182                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34183                     scale = targetWidth / width;
34184                 }
34185                 
34186                 if(x > 0 && y == 0){
34187                     scale = targetHeight / height;
34188                 }
34189                 
34190                 if(x > 0 && y > 0){
34191                     scale = targetWidth / width;
34192                     
34193                     if(width < height){
34194                         scale = targetHeight / height;
34195                     }
34196                 }
34197                 
34198                 context.scale(scale, scale);
34199                 
34200                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34201                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34202
34203                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34204                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34205                 
34206                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34207                 
34208                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34209                 
34210                 break;
34211             case 180 :
34212                 
34213                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34214                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34215                 
34216                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34217                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34218                 
34219                 var targetWidth = this.minWidth - 2 * x;
34220                 var targetHeight = this.minHeight - 2 * y;
34221                 
34222                 var scale = 1;
34223                 
34224                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34225                     scale = targetWidth / width;
34226                 }
34227                 
34228                 if(x > 0 && y == 0){
34229                     scale = targetHeight / height;
34230                 }
34231                 
34232                 if(x > 0 && y > 0){
34233                     scale = targetWidth / width;
34234                     
34235                     if(width < height){
34236                         scale = targetHeight / height;
34237                     }
34238                 }
34239                 
34240                 context.scale(scale, scale);
34241                 
34242                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34243                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34244
34245                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34246                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34247
34248                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34249                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34250                 
34251                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34252                 
34253                 break;
34254             case 270 :
34255                 
34256                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34257                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34258                 
34259                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34260                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34261                 
34262                 var targetWidth = this.minWidth - 2 * x;
34263                 var targetHeight = this.minHeight - 2 * y;
34264                 
34265                 var scale = 1;
34266                 
34267                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34268                     scale = targetWidth / width;
34269                 }
34270                 
34271                 if(x > 0 && y == 0){
34272                     scale = targetHeight / height;
34273                 }
34274                 
34275                 if(x > 0 && y > 0){
34276                     scale = targetWidth / width;
34277                     
34278                     if(width < height){
34279                         scale = targetHeight / height;
34280                     }
34281                 }
34282                 
34283                 context.scale(scale, scale);
34284                 
34285                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34286                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34287
34288                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34289                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34290                 
34291                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34292                 
34293                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34294                 
34295                 break;
34296             default : 
34297                 break;
34298         }
34299         
34300         this.cropData = canvas.toDataURL(this.cropType);
34301         
34302         if(this.fireEvent('crop', this, this.cropData) !== false){
34303             this.process(this.file, this.cropData);
34304         }
34305         
34306         return;
34307         
34308     },
34309     
34310     setThumbBoxSize : function()
34311     {
34312         var width, height;
34313         
34314         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
34315             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
34316             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
34317             
34318             this.minWidth = width;
34319             this.minHeight = height;
34320             
34321             if(this.rotate == 90 || this.rotate == 270){
34322                 this.minWidth = height;
34323                 this.minHeight = width;
34324             }
34325         }
34326         
34327         height = 300;
34328         width = Math.ceil(this.minWidth * height / this.minHeight);
34329         
34330         if(this.minWidth > this.minHeight){
34331             width = 300;
34332             height = Math.ceil(this.minHeight * width / this.minWidth);
34333         }
34334         
34335         this.thumbEl.setStyle({
34336             width : width + 'px',
34337             height : height + 'px'
34338         });
34339
34340         return;
34341             
34342     },
34343     
34344     setThumbBoxPosition : function()
34345     {
34346         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
34347         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
34348         
34349         this.thumbEl.setLeft(x);
34350         this.thumbEl.setTop(y);
34351         
34352     },
34353     
34354     baseRotateLevel : function()
34355     {
34356         this.baseRotate = 1;
34357         
34358         if(
34359                 typeof(this.exif) != 'undefined' &&
34360                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
34361                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
34362         ){
34363             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
34364         }
34365         
34366         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
34367         
34368     },
34369     
34370     baseScaleLevel : function()
34371     {
34372         var width, height;
34373         
34374         if(this.isDocument){
34375             
34376             if(this.baseRotate == 6 || this.baseRotate == 8){
34377             
34378                 height = this.thumbEl.getHeight();
34379                 this.baseScale = height / this.imageEl.OriginWidth;
34380
34381                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
34382                     width = this.thumbEl.getWidth();
34383                     this.baseScale = width / this.imageEl.OriginHeight;
34384                 }
34385
34386                 return;
34387             }
34388
34389             height = this.thumbEl.getHeight();
34390             this.baseScale = height / this.imageEl.OriginHeight;
34391
34392             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
34393                 width = this.thumbEl.getWidth();
34394                 this.baseScale = width / this.imageEl.OriginWidth;
34395             }
34396
34397             return;
34398         }
34399         
34400         if(this.baseRotate == 6 || this.baseRotate == 8){
34401             
34402             width = this.thumbEl.getHeight();
34403             this.baseScale = width / this.imageEl.OriginHeight;
34404             
34405             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
34406                 height = this.thumbEl.getWidth();
34407                 this.baseScale = height / this.imageEl.OriginHeight;
34408             }
34409             
34410             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34411                 height = this.thumbEl.getWidth();
34412                 this.baseScale = height / this.imageEl.OriginHeight;
34413                 
34414                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
34415                     width = this.thumbEl.getHeight();
34416                     this.baseScale = width / this.imageEl.OriginWidth;
34417                 }
34418             }
34419             
34420             return;
34421         }
34422         
34423         width = this.thumbEl.getWidth();
34424         this.baseScale = width / this.imageEl.OriginWidth;
34425         
34426         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
34427             height = this.thumbEl.getHeight();
34428             this.baseScale = height / this.imageEl.OriginHeight;
34429         }
34430         
34431         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34432             
34433             height = this.thumbEl.getHeight();
34434             this.baseScale = height / this.imageEl.OriginHeight;
34435             
34436             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
34437                 width = this.thumbEl.getWidth();
34438                 this.baseScale = width / this.imageEl.OriginWidth;
34439             }
34440             
34441         }
34442         
34443         return;
34444     },
34445     
34446     getScaleLevel : function()
34447     {
34448         return this.baseScale * Math.pow(1.1, this.scale);
34449     },
34450     
34451     onTouchStart : function(e)
34452     {
34453         if(!this.canvasLoaded){
34454             this.beforeSelectFile(e);
34455             return;
34456         }
34457         
34458         var touches = e.browserEvent.touches;
34459         
34460         if(!touches){
34461             return;
34462         }
34463         
34464         if(touches.length == 1){
34465             this.onMouseDown(e);
34466             return;
34467         }
34468         
34469         if(touches.length != 2){
34470             return;
34471         }
34472         
34473         var coords = [];
34474         
34475         for(var i = 0, finger; finger = touches[i]; i++){
34476             coords.push(finger.pageX, finger.pageY);
34477         }
34478         
34479         var x = Math.pow(coords[0] - coords[2], 2);
34480         var y = Math.pow(coords[1] - coords[3], 2);
34481         
34482         this.startDistance = Math.sqrt(x + y);
34483         
34484         this.startScale = this.scale;
34485         
34486         this.pinching = true;
34487         this.dragable = false;
34488         
34489     },
34490     
34491     onTouchMove : function(e)
34492     {
34493         if(!this.pinching && !this.dragable){
34494             return;
34495         }
34496         
34497         var touches = e.browserEvent.touches;
34498         
34499         if(!touches){
34500             return;
34501         }
34502         
34503         if(this.dragable){
34504             this.onMouseMove(e);
34505             return;
34506         }
34507         
34508         var coords = [];
34509         
34510         for(var i = 0, finger; finger = touches[i]; i++){
34511             coords.push(finger.pageX, finger.pageY);
34512         }
34513         
34514         var x = Math.pow(coords[0] - coords[2], 2);
34515         var y = Math.pow(coords[1] - coords[3], 2);
34516         
34517         this.endDistance = Math.sqrt(x + y);
34518         
34519         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
34520         
34521         if(!this.zoomable()){
34522             this.scale = this.startScale;
34523             return;
34524         }
34525         
34526         this.draw();
34527         
34528     },
34529     
34530     onTouchEnd : function(e)
34531     {
34532         this.pinching = false;
34533         this.dragable = false;
34534         
34535     },
34536     
34537     process : function(file, crop)
34538     {
34539         if(this.loadMask){
34540             this.maskEl.mask(this.loadingText);
34541         }
34542         
34543         this.xhr = new XMLHttpRequest();
34544         
34545         file.xhr = this.xhr;
34546
34547         this.xhr.open(this.method, this.url, true);
34548         
34549         var headers = {
34550             "Accept": "application/json",
34551             "Cache-Control": "no-cache",
34552             "X-Requested-With": "XMLHttpRequest"
34553         };
34554         
34555         for (var headerName in headers) {
34556             var headerValue = headers[headerName];
34557             if (headerValue) {
34558                 this.xhr.setRequestHeader(headerName, headerValue);
34559             }
34560         }
34561         
34562         var _this = this;
34563         
34564         this.xhr.onload = function()
34565         {
34566             _this.xhrOnLoad(_this.xhr);
34567         }
34568         
34569         this.xhr.onerror = function()
34570         {
34571             _this.xhrOnError(_this.xhr);
34572         }
34573         
34574         var formData = new FormData();
34575
34576         formData.append('returnHTML', 'NO');
34577         
34578         if(crop){
34579             formData.append('crop', crop);
34580         }
34581         
34582         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
34583             formData.append(this.paramName, file, file.name);
34584         }
34585         
34586         if(typeof(file.filename) != 'undefined'){
34587             formData.append('filename', file.filename);
34588         }
34589         
34590         if(typeof(file.mimetype) != 'undefined'){
34591             formData.append('mimetype', file.mimetype);
34592         }
34593         
34594         if(this.fireEvent('arrange', this, formData) != false){
34595             this.xhr.send(formData);
34596         };
34597     },
34598     
34599     xhrOnLoad : function(xhr)
34600     {
34601         if(this.loadMask){
34602             this.maskEl.unmask();
34603         }
34604         
34605         if (xhr.readyState !== 4) {
34606             this.fireEvent('exception', this, xhr);
34607             return;
34608         }
34609
34610         var response = Roo.decode(xhr.responseText);
34611         
34612         if(!response.success){
34613             this.fireEvent('exception', this, xhr);
34614             return;
34615         }
34616         
34617         var response = Roo.decode(xhr.responseText);
34618         
34619         this.fireEvent('upload', this, response);
34620         
34621     },
34622     
34623     xhrOnError : function()
34624     {
34625         if(this.loadMask){
34626             this.maskEl.unmask();
34627         }
34628         
34629         Roo.log('xhr on error');
34630         
34631         var response = Roo.decode(xhr.responseText);
34632           
34633         Roo.log(response);
34634         
34635     },
34636     
34637     prepare : function(file)
34638     {   
34639         if(this.loadMask){
34640             this.maskEl.mask(this.loadingText);
34641         }
34642         
34643         this.file = false;
34644         this.exif = {};
34645         
34646         if(typeof(file) === 'string'){
34647             this.loadCanvas(file);
34648             return;
34649         }
34650         
34651         if(!file || !this.urlAPI){
34652             return;
34653         }
34654         
34655         this.file = file;
34656         this.cropType = file.type;
34657         
34658         var _this = this;
34659         
34660         if(this.fireEvent('prepare', this, this.file) != false){
34661             
34662             var reader = new FileReader();
34663             
34664             reader.onload = function (e) {
34665                 if (e.target.error) {
34666                     Roo.log(e.target.error);
34667                     return;
34668                 }
34669                 
34670                 var buffer = e.target.result,
34671                     dataView = new DataView(buffer),
34672                     offset = 2,
34673                     maxOffset = dataView.byteLength - 4,
34674                     markerBytes,
34675                     markerLength;
34676                 
34677                 if (dataView.getUint16(0) === 0xffd8) {
34678                     while (offset < maxOffset) {
34679                         markerBytes = dataView.getUint16(offset);
34680                         
34681                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
34682                             markerLength = dataView.getUint16(offset + 2) + 2;
34683                             if (offset + markerLength > dataView.byteLength) {
34684                                 Roo.log('Invalid meta data: Invalid segment size.');
34685                                 break;
34686                             }
34687                             
34688                             if(markerBytes == 0xffe1){
34689                                 _this.parseExifData(
34690                                     dataView,
34691                                     offset,
34692                                     markerLength
34693                                 );
34694                             }
34695                             
34696                             offset += markerLength;
34697                             
34698                             continue;
34699                         }
34700                         
34701                         break;
34702                     }
34703                     
34704                 }
34705                 
34706                 var url = _this.urlAPI.createObjectURL(_this.file);
34707                 
34708                 _this.loadCanvas(url);
34709                 
34710                 return;
34711             }
34712             
34713             reader.readAsArrayBuffer(this.file);
34714             
34715         }
34716         
34717     },
34718     
34719     parseExifData : function(dataView, offset, length)
34720     {
34721         var tiffOffset = offset + 10,
34722             littleEndian,
34723             dirOffset;
34724     
34725         if (dataView.getUint32(offset + 4) !== 0x45786966) {
34726             // No Exif data, might be XMP data instead
34727             return;
34728         }
34729         
34730         // Check for the ASCII code for "Exif" (0x45786966):
34731         if (dataView.getUint32(offset + 4) !== 0x45786966) {
34732             // No Exif data, might be XMP data instead
34733             return;
34734         }
34735         if (tiffOffset + 8 > dataView.byteLength) {
34736             Roo.log('Invalid Exif data: Invalid segment size.');
34737             return;
34738         }
34739         // Check for the two null bytes:
34740         if (dataView.getUint16(offset + 8) !== 0x0000) {
34741             Roo.log('Invalid Exif data: Missing byte alignment offset.');
34742             return;
34743         }
34744         // Check the byte alignment:
34745         switch (dataView.getUint16(tiffOffset)) {
34746         case 0x4949:
34747             littleEndian = true;
34748             break;
34749         case 0x4D4D:
34750             littleEndian = false;
34751             break;
34752         default:
34753             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
34754             return;
34755         }
34756         // Check for the TIFF tag marker (0x002A):
34757         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
34758             Roo.log('Invalid Exif data: Missing TIFF marker.');
34759             return;
34760         }
34761         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
34762         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
34763         
34764         this.parseExifTags(
34765             dataView,
34766             tiffOffset,
34767             tiffOffset + dirOffset,
34768             littleEndian
34769         );
34770     },
34771     
34772     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
34773     {
34774         var tagsNumber,
34775             dirEndOffset,
34776             i;
34777         if (dirOffset + 6 > dataView.byteLength) {
34778             Roo.log('Invalid Exif data: Invalid directory offset.');
34779             return;
34780         }
34781         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
34782         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
34783         if (dirEndOffset + 4 > dataView.byteLength) {
34784             Roo.log('Invalid Exif data: Invalid directory size.');
34785             return;
34786         }
34787         for (i = 0; i < tagsNumber; i += 1) {
34788             this.parseExifTag(
34789                 dataView,
34790                 tiffOffset,
34791                 dirOffset + 2 + 12 * i, // tag offset
34792                 littleEndian
34793             );
34794         }
34795         // Return the offset to the next directory:
34796         return dataView.getUint32(dirEndOffset, littleEndian);
34797     },
34798     
34799     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
34800     {
34801         var tag = dataView.getUint16(offset, littleEndian);
34802         
34803         this.exif[tag] = this.getExifValue(
34804             dataView,
34805             tiffOffset,
34806             offset,
34807             dataView.getUint16(offset + 2, littleEndian), // tag type
34808             dataView.getUint32(offset + 4, littleEndian), // tag length
34809             littleEndian
34810         );
34811     },
34812     
34813     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
34814     {
34815         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
34816             tagSize,
34817             dataOffset,
34818             values,
34819             i,
34820             str,
34821             c;
34822     
34823         if (!tagType) {
34824             Roo.log('Invalid Exif data: Invalid tag type.');
34825             return;
34826         }
34827         
34828         tagSize = tagType.size * length;
34829         // Determine if the value is contained in the dataOffset bytes,
34830         // or if the value at the dataOffset is a pointer to the actual data:
34831         dataOffset = tagSize > 4 ?
34832                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
34833         if (dataOffset + tagSize > dataView.byteLength) {
34834             Roo.log('Invalid Exif data: Invalid data offset.');
34835             return;
34836         }
34837         if (length === 1) {
34838             return tagType.getValue(dataView, dataOffset, littleEndian);
34839         }
34840         values = [];
34841         for (i = 0; i < length; i += 1) {
34842             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
34843         }
34844         
34845         if (tagType.ascii) {
34846             str = '';
34847             // Concatenate the chars:
34848             for (i = 0; i < values.length; i += 1) {
34849                 c = values[i];
34850                 // Ignore the terminating NULL byte(s):
34851                 if (c === '\u0000') {
34852                     break;
34853                 }
34854                 str += c;
34855             }
34856             return str;
34857         }
34858         return values;
34859     }
34860     
34861 });
34862
34863 Roo.apply(Roo.bootstrap.UploadCropbox, {
34864     tags : {
34865         'Orientation': 0x0112
34866     },
34867     
34868     Orientation: {
34869             1: 0, //'top-left',
34870 //            2: 'top-right',
34871             3: 180, //'bottom-right',
34872 //            4: 'bottom-left',
34873 //            5: 'left-top',
34874             6: 90, //'right-top',
34875 //            7: 'right-bottom',
34876             8: 270 //'left-bottom'
34877     },
34878     
34879     exifTagTypes : {
34880         // byte, 8-bit unsigned int:
34881         1: {
34882             getValue: function (dataView, dataOffset) {
34883                 return dataView.getUint8(dataOffset);
34884             },
34885             size: 1
34886         },
34887         // ascii, 8-bit byte:
34888         2: {
34889             getValue: function (dataView, dataOffset) {
34890                 return String.fromCharCode(dataView.getUint8(dataOffset));
34891             },
34892             size: 1,
34893             ascii: true
34894         },
34895         // short, 16 bit int:
34896         3: {
34897             getValue: function (dataView, dataOffset, littleEndian) {
34898                 return dataView.getUint16(dataOffset, littleEndian);
34899             },
34900             size: 2
34901         },
34902         // long, 32 bit int:
34903         4: {
34904             getValue: function (dataView, dataOffset, littleEndian) {
34905                 return dataView.getUint32(dataOffset, littleEndian);
34906             },
34907             size: 4
34908         },
34909         // rational = two long values, first is numerator, second is denominator:
34910         5: {
34911             getValue: function (dataView, dataOffset, littleEndian) {
34912                 return dataView.getUint32(dataOffset, littleEndian) /
34913                     dataView.getUint32(dataOffset + 4, littleEndian);
34914             },
34915             size: 8
34916         },
34917         // slong, 32 bit signed int:
34918         9: {
34919             getValue: function (dataView, dataOffset, littleEndian) {
34920                 return dataView.getInt32(dataOffset, littleEndian);
34921             },
34922             size: 4
34923         },
34924         // srational, two slongs, first is numerator, second is denominator:
34925         10: {
34926             getValue: function (dataView, dataOffset, littleEndian) {
34927                 return dataView.getInt32(dataOffset, littleEndian) /
34928                     dataView.getInt32(dataOffset + 4, littleEndian);
34929             },
34930             size: 8
34931         }
34932     },
34933     
34934     footer : {
34935         STANDARD : [
34936             {
34937                 tag : 'div',
34938                 cls : 'btn-group roo-upload-cropbox-rotate-left',
34939                 action : 'rotate-left',
34940                 cn : [
34941                     {
34942                         tag : 'button',
34943                         cls : 'btn btn-default',
34944                         html : '<i class="fa fa-undo"></i>'
34945                     }
34946                 ]
34947             },
34948             {
34949                 tag : 'div',
34950                 cls : 'btn-group roo-upload-cropbox-picture',
34951                 action : 'picture',
34952                 cn : [
34953                     {
34954                         tag : 'button',
34955                         cls : 'btn btn-default',
34956                         html : '<i class="fa fa-picture-o"></i>'
34957                     }
34958                 ]
34959             },
34960             {
34961                 tag : 'div',
34962                 cls : 'btn-group roo-upload-cropbox-rotate-right',
34963                 action : 'rotate-right',
34964                 cn : [
34965                     {
34966                         tag : 'button',
34967                         cls : 'btn btn-default',
34968                         html : '<i class="fa fa-repeat"></i>'
34969                     }
34970                 ]
34971             }
34972         ],
34973         DOCUMENT : [
34974             {
34975                 tag : 'div',
34976                 cls : 'btn-group roo-upload-cropbox-rotate-left',
34977                 action : 'rotate-left',
34978                 cn : [
34979                     {
34980                         tag : 'button',
34981                         cls : 'btn btn-default',
34982                         html : '<i class="fa fa-undo"></i>'
34983                     }
34984                 ]
34985             },
34986             {
34987                 tag : 'div',
34988                 cls : 'btn-group roo-upload-cropbox-download',
34989                 action : 'download',
34990                 cn : [
34991                     {
34992                         tag : 'button',
34993                         cls : 'btn btn-default',
34994                         html : '<i class="fa fa-download"></i>'
34995                     }
34996                 ]
34997             },
34998             {
34999                 tag : 'div',
35000                 cls : 'btn-group roo-upload-cropbox-crop',
35001                 action : 'crop',
35002                 cn : [
35003                     {
35004                         tag : 'button',
35005                         cls : 'btn btn-default',
35006                         html : '<i class="fa fa-crop"></i>'
35007                     }
35008                 ]
35009             },
35010             {
35011                 tag : 'div',
35012                 cls : 'btn-group roo-upload-cropbox-trash',
35013                 action : 'trash',
35014                 cn : [
35015                     {
35016                         tag : 'button',
35017                         cls : 'btn btn-default',
35018                         html : '<i class="fa fa-trash"></i>'
35019                     }
35020                 ]
35021             },
35022             {
35023                 tag : 'div',
35024                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35025                 action : 'rotate-right',
35026                 cn : [
35027                     {
35028                         tag : 'button',
35029                         cls : 'btn btn-default',
35030                         html : '<i class="fa fa-repeat"></i>'
35031                     }
35032                 ]
35033             }
35034         ],
35035         ROTATOR : [
35036             {
35037                 tag : 'div',
35038                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35039                 action : 'rotate-left',
35040                 cn : [
35041                     {
35042                         tag : 'button',
35043                         cls : 'btn btn-default',
35044                         html : '<i class="fa fa-undo"></i>'
35045                     }
35046                 ]
35047             },
35048             {
35049                 tag : 'div',
35050                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35051                 action : 'rotate-right',
35052                 cn : [
35053                     {
35054                         tag : 'button',
35055                         cls : 'btn btn-default',
35056                         html : '<i class="fa fa-repeat"></i>'
35057                     }
35058                 ]
35059             }
35060         ]
35061     }
35062 });
35063
35064 /*
35065 * Licence: LGPL
35066 */
35067
35068 /**
35069  * @class Roo.bootstrap.DocumentManager
35070  * @extends Roo.bootstrap.Component
35071  * Bootstrap DocumentManager class
35072  * @cfg {String} paramName default 'imageUpload'
35073  * @cfg {String} toolTipName default 'filename'
35074  * @cfg {String} method default POST
35075  * @cfg {String} url action url
35076  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
35077  * @cfg {Boolean} multiple multiple upload default true
35078  * @cfg {Number} thumbSize default 300
35079  * @cfg {String} fieldLabel
35080  * @cfg {Number} labelWidth default 4
35081  * @cfg {String} labelAlign (left|top) default left
35082  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
35083 * @cfg {Number} labellg set the width of label (1-12)
35084  * @cfg {Number} labelmd set the width of label (1-12)
35085  * @cfg {Number} labelsm set the width of label (1-12)
35086  * @cfg {Number} labelxs set the width of label (1-12)
35087  * 
35088  * @constructor
35089  * Create a new DocumentManager
35090  * @param {Object} config The config object
35091  */
35092
35093 Roo.bootstrap.DocumentManager = function(config){
35094     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
35095     
35096     this.files = [];
35097     this.delegates = [];
35098     
35099     this.addEvents({
35100         /**
35101          * @event initial
35102          * Fire when initial the DocumentManager
35103          * @param {Roo.bootstrap.DocumentManager} this
35104          */
35105         "initial" : true,
35106         /**
35107          * @event inspect
35108          * inspect selected file
35109          * @param {Roo.bootstrap.DocumentManager} this
35110          * @param {File} file
35111          */
35112         "inspect" : true,
35113         /**
35114          * @event exception
35115          * Fire when xhr load exception
35116          * @param {Roo.bootstrap.DocumentManager} this
35117          * @param {XMLHttpRequest} xhr
35118          */
35119         "exception" : true,
35120         /**
35121          * @event afterupload
35122          * Fire when xhr load exception
35123          * @param {Roo.bootstrap.DocumentManager} this
35124          * @param {XMLHttpRequest} xhr
35125          */
35126         "afterupload" : true,
35127         /**
35128          * @event prepare
35129          * prepare the form data
35130          * @param {Roo.bootstrap.DocumentManager} this
35131          * @param {Object} formData
35132          */
35133         "prepare" : true,
35134         /**
35135          * @event remove
35136          * Fire when remove the file
35137          * @param {Roo.bootstrap.DocumentManager} this
35138          * @param {Object} file
35139          */
35140         "remove" : true,
35141         /**
35142          * @event refresh
35143          * Fire after refresh the file
35144          * @param {Roo.bootstrap.DocumentManager} this
35145          */
35146         "refresh" : true,
35147         /**
35148          * @event click
35149          * Fire after click the image
35150          * @param {Roo.bootstrap.DocumentManager} this
35151          * @param {Object} file
35152          */
35153         "click" : true,
35154         /**
35155          * @event edit
35156          * Fire when upload a image and editable set to true
35157          * @param {Roo.bootstrap.DocumentManager} this
35158          * @param {Object} file
35159          */
35160         "edit" : true,
35161         /**
35162          * @event beforeselectfile
35163          * Fire before select file
35164          * @param {Roo.bootstrap.DocumentManager} this
35165          */
35166         "beforeselectfile" : true,
35167         /**
35168          * @event process
35169          * Fire before process file
35170          * @param {Roo.bootstrap.DocumentManager} this
35171          * @param {Object} file
35172          */
35173         "process" : true,
35174         /**
35175          * @event previewrendered
35176          * Fire when preview rendered
35177          * @param {Roo.bootstrap.DocumentManager} this
35178          * @param {Object} file
35179          */
35180         "previewrendered" : true,
35181         /**
35182          */
35183         "previewResize" : true
35184         
35185     });
35186 };
35187
35188 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
35189     
35190     boxes : 0,
35191     inputName : '',
35192     thumbSize : 300,
35193     multiple : true,
35194     files : false,
35195     method : 'POST',
35196     url : '',
35197     paramName : 'imageUpload',
35198     toolTipName : 'filename',
35199     fieldLabel : '',
35200     labelWidth : 4,
35201     labelAlign : 'left',
35202     editable : true,
35203     delegates : false,
35204     xhr : false, 
35205     
35206     labellg : 0,
35207     labelmd : 0,
35208     labelsm : 0,
35209     labelxs : 0,
35210     
35211     getAutoCreate : function()
35212     {   
35213         var managerWidget = {
35214             tag : 'div',
35215             cls : 'roo-document-manager',
35216             cn : [
35217                 {
35218                     tag : 'input',
35219                     cls : 'roo-document-manager-selector',
35220                     type : 'file'
35221                 },
35222                 {
35223                     tag : 'div',
35224                     cls : 'roo-document-manager-uploader',
35225                     cn : [
35226                         {
35227                             tag : 'div',
35228                             cls : 'roo-document-manager-upload-btn',
35229                             html : '<i class="fa fa-plus"></i>'
35230                         }
35231                     ]
35232                     
35233                 }
35234             ]
35235         };
35236         
35237         var content = [
35238             {
35239                 tag : 'div',
35240                 cls : 'column col-md-12',
35241                 cn : managerWidget
35242             }
35243         ];
35244         
35245         if(this.fieldLabel.length){
35246             
35247             content = [
35248                 {
35249                     tag : 'div',
35250                     cls : 'column col-md-12',
35251                     html : this.fieldLabel
35252                 },
35253                 {
35254                     tag : 'div',
35255                     cls : 'column col-md-12',
35256                     cn : managerWidget
35257                 }
35258             ];
35259
35260             if(this.labelAlign == 'left'){
35261                 content = [
35262                     {
35263                         tag : 'div',
35264                         cls : 'column',
35265                         html : this.fieldLabel
35266                     },
35267                     {
35268                         tag : 'div',
35269                         cls : 'column',
35270                         cn : managerWidget
35271                     }
35272                 ];
35273                 
35274                 if(this.labelWidth > 12){
35275                     content[0].style = "width: " + this.labelWidth + 'px';
35276                 }
35277
35278                 if(this.labelWidth < 13 && this.labelmd == 0){
35279                     this.labelmd = this.labelWidth;
35280                 }
35281
35282                 if(this.labellg > 0){
35283                     content[0].cls += ' col-lg-' + this.labellg;
35284                     content[1].cls += ' col-lg-' + (12 - this.labellg);
35285                 }
35286
35287                 if(this.labelmd > 0){
35288                     content[0].cls += ' col-md-' + this.labelmd;
35289                     content[1].cls += ' col-md-' + (12 - this.labelmd);
35290                 }
35291
35292                 if(this.labelsm > 0){
35293                     content[0].cls += ' col-sm-' + this.labelsm;
35294                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
35295                 }
35296
35297                 if(this.labelxs > 0){
35298                     content[0].cls += ' col-xs-' + this.labelxs;
35299                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
35300                 }
35301                 
35302             }
35303         }
35304         
35305         var cfg = {
35306             tag : 'div',
35307             cls : 'row clearfix',
35308             cn : content
35309         };
35310         
35311         return cfg;
35312         
35313     },
35314     
35315     initEvents : function()
35316     {
35317         this.managerEl = this.el.select('.roo-document-manager', true).first();
35318         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35319         
35320         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
35321         this.selectorEl.hide();
35322         
35323         if(this.multiple){
35324             this.selectorEl.attr('multiple', 'multiple');
35325         }
35326         
35327         this.selectorEl.on('change', this.onFileSelected, this);
35328         
35329         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
35330         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35331         
35332         this.uploader.on('click', this.onUploaderClick, this);
35333         
35334         this.renderProgressDialog();
35335         
35336         var _this = this;
35337         
35338         window.addEventListener("resize", function() { _this.refresh(); } );
35339         
35340         this.fireEvent('initial', this);
35341     },
35342     
35343     renderProgressDialog : function()
35344     {
35345         var _this = this;
35346         
35347         this.progressDialog = new Roo.bootstrap.Modal({
35348             cls : 'roo-document-manager-progress-dialog',
35349             allow_close : false,
35350             animate : false,
35351             title : '',
35352             buttons : [
35353                 {
35354                     name  :'cancel',
35355                     weight : 'danger',
35356                     html : 'Cancel'
35357                 }
35358             ], 
35359             listeners : { 
35360                 btnclick : function() {
35361                     _this.uploadCancel();
35362                     this.hide();
35363                 }
35364             }
35365         });
35366          
35367         this.progressDialog.render(Roo.get(document.body));
35368          
35369         this.progress = new Roo.bootstrap.Progress({
35370             cls : 'roo-document-manager-progress',
35371             active : true,
35372             striped : true
35373         });
35374         
35375         this.progress.render(this.progressDialog.getChildContainer());
35376         
35377         this.progressBar = new Roo.bootstrap.ProgressBar({
35378             cls : 'roo-document-manager-progress-bar',
35379             aria_valuenow : 0,
35380             aria_valuemin : 0,
35381             aria_valuemax : 12,
35382             panel : 'success'
35383         });
35384         
35385         this.progressBar.render(this.progress.getChildContainer());
35386     },
35387     
35388     onUploaderClick : function(e)
35389     {
35390         e.preventDefault();
35391      
35392         if(this.fireEvent('beforeselectfile', this) != false){
35393             this.selectorEl.dom.click();
35394         }
35395         
35396     },
35397     
35398     onFileSelected : function(e)
35399     {
35400         e.preventDefault();
35401         
35402         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35403             return;
35404         }
35405         
35406         Roo.each(this.selectorEl.dom.files, function(file){
35407             if(this.fireEvent('inspect', this, file) != false){
35408                 this.files.push(file);
35409             }
35410         }, this);
35411         
35412         this.queue();
35413         
35414     },
35415     
35416     queue : function()
35417     {
35418         this.selectorEl.dom.value = '';
35419         
35420         if(!this.files || !this.files.length){
35421             return;
35422         }
35423         
35424         if(this.boxes > 0 && this.files.length > this.boxes){
35425             this.files = this.files.slice(0, this.boxes);
35426         }
35427         
35428         this.uploader.show();
35429         
35430         if(this.boxes > 0 && this.files.length > this.boxes - 1){
35431             this.uploader.hide();
35432         }
35433         
35434         var _this = this;
35435         
35436         var files = [];
35437         
35438         var docs = [];
35439         
35440         Roo.each(this.files, function(file){
35441             
35442             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35443                 var f = this.renderPreview(file);
35444                 files.push(f);
35445                 return;
35446             }
35447             
35448             if(file.type.indexOf('image') != -1){
35449                 this.delegates.push(
35450                     (function(){
35451                         _this.process(file);
35452                     }).createDelegate(this)
35453                 );
35454         
35455                 return;
35456             }
35457             
35458             docs.push(
35459                 (function(){
35460                     _this.process(file);
35461                 }).createDelegate(this)
35462             );
35463             
35464         }, this);
35465         
35466         this.files = files;
35467         
35468         this.delegates = this.delegates.concat(docs);
35469         
35470         if(!this.delegates.length){
35471             this.refresh();
35472             return;
35473         }
35474         
35475         this.progressBar.aria_valuemax = this.delegates.length;
35476         
35477         this.arrange();
35478         
35479         return;
35480     },
35481     
35482     arrange : function()
35483     {
35484         if(!this.delegates.length){
35485             this.progressDialog.hide();
35486             this.refresh();
35487             return;
35488         }
35489         
35490         var delegate = this.delegates.shift();
35491         
35492         this.progressDialog.show();
35493         
35494         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
35495         
35496         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
35497         
35498         delegate();
35499     },
35500     
35501     refresh : function()
35502     {
35503         this.uploader.show();
35504         
35505         if(this.boxes > 0 && this.files.length > this.boxes - 1){
35506             this.uploader.hide();
35507         }
35508         
35509         Roo.isTouch ? this.closable(false) : this.closable(true);
35510         
35511         this.fireEvent('refresh', this);
35512     },
35513     
35514     onRemove : function(e, el, o)
35515     {
35516         e.preventDefault();
35517         
35518         this.fireEvent('remove', this, o);
35519         
35520     },
35521     
35522     remove : function(o)
35523     {
35524         var files = [];
35525         
35526         Roo.each(this.files, function(file){
35527             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
35528                 files.push(file);
35529                 return;
35530             }
35531
35532             o.target.remove();
35533
35534         }, this);
35535         
35536         this.files = files;
35537         
35538         this.refresh();
35539     },
35540     
35541     clear : function()
35542     {
35543         Roo.each(this.files, function(file){
35544             if(!file.target){
35545                 return;
35546             }
35547             
35548             file.target.remove();
35549
35550         }, this);
35551         
35552         this.files = [];
35553         
35554         this.refresh();
35555     },
35556     
35557     onClick : function(e, el, o)
35558     {
35559         e.preventDefault();
35560         
35561         this.fireEvent('click', this, o);
35562         
35563     },
35564     
35565     closable : function(closable)
35566     {
35567         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
35568             
35569             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35570             
35571             if(closable){
35572                 el.show();
35573                 return;
35574             }
35575             
35576             el.hide();
35577             
35578         }, this);
35579     },
35580     
35581     xhrOnLoad : function(xhr)
35582     {
35583         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35584             el.remove();
35585         }, this);
35586         
35587         if (xhr.readyState !== 4) {
35588             this.arrange();
35589             this.fireEvent('exception', this, xhr);
35590             return;
35591         }
35592
35593         var response = Roo.decode(xhr.responseText);
35594         
35595         if(!response.success){
35596             this.arrange();
35597             this.fireEvent('exception', this, xhr);
35598             return;
35599         }
35600         
35601         var file = this.renderPreview(response.data);
35602         
35603         this.files.push(file);
35604         
35605         this.arrange();
35606         
35607         this.fireEvent('afterupload', this, xhr);
35608         
35609     },
35610     
35611     xhrOnError : function(xhr)
35612     {
35613         Roo.log('xhr on error');
35614         
35615         var response = Roo.decode(xhr.responseText);
35616           
35617         Roo.log(response);
35618         
35619         this.arrange();
35620     },
35621     
35622     process : function(file)
35623     {
35624         if(this.fireEvent('process', this, file) !== false){
35625             if(this.editable && file.type.indexOf('image') != -1){
35626                 this.fireEvent('edit', this, file);
35627                 return;
35628             }
35629
35630             this.uploadStart(file, false);
35631
35632             return;
35633         }
35634         
35635     },
35636     
35637     uploadStart : function(file, crop)
35638     {
35639         this.xhr = new XMLHttpRequest();
35640         
35641         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35642             this.arrange();
35643             return;
35644         }
35645         
35646         file.xhr = this.xhr;
35647             
35648         this.managerEl.createChild({
35649             tag : 'div',
35650             cls : 'roo-document-manager-loading',
35651             cn : [
35652                 {
35653                     tag : 'div',
35654                     tooltip : file.name,
35655                     cls : 'roo-document-manager-thumb',
35656                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
35657                 }
35658             ]
35659
35660         });
35661
35662         this.xhr.open(this.method, this.url, true);
35663         
35664         var headers = {
35665             "Accept": "application/json",
35666             "Cache-Control": "no-cache",
35667             "X-Requested-With": "XMLHttpRequest"
35668         };
35669         
35670         for (var headerName in headers) {
35671             var headerValue = headers[headerName];
35672             if (headerValue) {
35673                 this.xhr.setRequestHeader(headerName, headerValue);
35674             }
35675         }
35676         
35677         var _this = this;
35678         
35679         this.xhr.onload = function()
35680         {
35681             _this.xhrOnLoad(_this.xhr);
35682         }
35683         
35684         this.xhr.onerror = function()
35685         {
35686             _this.xhrOnError(_this.xhr);
35687         }
35688         
35689         var formData = new FormData();
35690
35691         formData.append('returnHTML', 'NO');
35692         
35693         if(crop){
35694             formData.append('crop', crop);
35695         }
35696         
35697         formData.append(this.paramName, file, file.name);
35698         
35699         var options = {
35700             file : file, 
35701             manually : false
35702         };
35703         
35704         if(this.fireEvent('prepare', this, formData, options) != false){
35705             
35706             if(options.manually){
35707                 return;
35708             }
35709             
35710             this.xhr.send(formData);
35711             return;
35712         };
35713         
35714         this.uploadCancel();
35715     },
35716     
35717     uploadCancel : function()
35718     {
35719         if (this.xhr) {
35720             this.xhr.abort();
35721         }
35722         
35723         this.delegates = [];
35724         
35725         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35726             el.remove();
35727         }, this);
35728         
35729         this.arrange();
35730     },
35731     
35732     renderPreview : function(file)
35733     {
35734         if(typeof(file.target) != 'undefined' && file.target){
35735             return file;
35736         }
35737         
35738         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
35739         
35740         var previewEl = this.managerEl.createChild({
35741             tag : 'div',
35742             cls : 'roo-document-manager-preview',
35743             cn : [
35744                 {
35745                     tag : 'div',
35746                     tooltip : file[this.toolTipName],
35747                     cls : 'roo-document-manager-thumb',
35748                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
35749                 },
35750                 {
35751                     tag : 'button',
35752                     cls : 'close',
35753                     html : '<i class="fa fa-times-circle"></i>'
35754                 }
35755             ]
35756         });
35757
35758         var close = previewEl.select('button.close', true).first();
35759
35760         close.on('click', this.onRemove, this, file);
35761
35762         file.target = previewEl;
35763
35764         var image = previewEl.select('img', true).first();
35765         
35766         var _this = this;
35767         
35768         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
35769         
35770         image.on('click', this.onClick, this, file);
35771         
35772         this.fireEvent('previewrendered', this, file);
35773         
35774         return file;
35775         
35776     },
35777     
35778     onPreviewLoad : function(file, image)
35779     {
35780         if(typeof(file.target) == 'undefined' || !file.target){
35781             return;
35782         }
35783         
35784         var width = image.dom.naturalWidth || image.dom.width;
35785         var height = image.dom.naturalHeight || image.dom.height;
35786         
35787         if(!this.previewResize) {
35788             return;
35789         }
35790         
35791         if(width > height){
35792             file.target.addClass('wide');
35793             return;
35794         }
35795         
35796         file.target.addClass('tall');
35797         return;
35798         
35799     },
35800     
35801     uploadFromSource : function(file, crop)
35802     {
35803         this.xhr = new XMLHttpRequest();
35804         
35805         this.managerEl.createChild({
35806             tag : 'div',
35807             cls : 'roo-document-manager-loading',
35808             cn : [
35809                 {
35810                     tag : 'div',
35811                     tooltip : file.name,
35812                     cls : 'roo-document-manager-thumb',
35813                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
35814                 }
35815             ]
35816
35817         });
35818
35819         this.xhr.open(this.method, this.url, true);
35820         
35821         var headers = {
35822             "Accept": "application/json",
35823             "Cache-Control": "no-cache",
35824             "X-Requested-With": "XMLHttpRequest"
35825         };
35826         
35827         for (var headerName in headers) {
35828             var headerValue = headers[headerName];
35829             if (headerValue) {
35830                 this.xhr.setRequestHeader(headerName, headerValue);
35831             }
35832         }
35833         
35834         var _this = this;
35835         
35836         this.xhr.onload = function()
35837         {
35838             _this.xhrOnLoad(_this.xhr);
35839         }
35840         
35841         this.xhr.onerror = function()
35842         {
35843             _this.xhrOnError(_this.xhr);
35844         }
35845         
35846         var formData = new FormData();
35847
35848         formData.append('returnHTML', 'NO');
35849         
35850         formData.append('crop', crop);
35851         
35852         if(typeof(file.filename) != 'undefined'){
35853             formData.append('filename', file.filename);
35854         }
35855         
35856         if(typeof(file.mimetype) != 'undefined'){
35857             formData.append('mimetype', file.mimetype);
35858         }
35859         
35860         Roo.log(formData);
35861         
35862         if(this.fireEvent('prepare', this, formData) != false){
35863             this.xhr.send(formData);
35864         };
35865     }
35866 });
35867
35868 /*
35869 * Licence: LGPL
35870 */
35871
35872 /**
35873  * @class Roo.bootstrap.DocumentViewer
35874  * @extends Roo.bootstrap.Component
35875  * Bootstrap DocumentViewer class
35876  * @cfg {Boolean} showDownload (true|false) show download button (default true)
35877  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
35878  * 
35879  * @constructor
35880  * Create a new DocumentViewer
35881  * @param {Object} config The config object
35882  */
35883
35884 Roo.bootstrap.DocumentViewer = function(config){
35885     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
35886     
35887     this.addEvents({
35888         /**
35889          * @event initial
35890          * Fire after initEvent
35891          * @param {Roo.bootstrap.DocumentViewer} this
35892          */
35893         "initial" : true,
35894         /**
35895          * @event click
35896          * Fire after click
35897          * @param {Roo.bootstrap.DocumentViewer} this
35898          */
35899         "click" : true,
35900         /**
35901          * @event download
35902          * Fire after download button
35903          * @param {Roo.bootstrap.DocumentViewer} this
35904          */
35905         "download" : true,
35906         /**
35907          * @event trash
35908          * Fire after trash button
35909          * @param {Roo.bootstrap.DocumentViewer} this
35910          */
35911         "trash" : true
35912         
35913     });
35914 };
35915
35916 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
35917     
35918     showDownload : true,
35919     
35920     showTrash : true,
35921     
35922     getAutoCreate : function()
35923     {
35924         var cfg = {
35925             tag : 'div',
35926             cls : 'roo-document-viewer',
35927             cn : [
35928                 {
35929                     tag : 'div',
35930                     cls : 'roo-document-viewer-body',
35931                     cn : [
35932                         {
35933                             tag : 'div',
35934                             cls : 'roo-document-viewer-thumb',
35935                             cn : [
35936                                 {
35937                                     tag : 'img',
35938                                     cls : 'roo-document-viewer-image'
35939                                 }
35940                             ]
35941                         }
35942                     ]
35943                 },
35944                 {
35945                     tag : 'div',
35946                     cls : 'roo-document-viewer-footer',
35947                     cn : {
35948                         tag : 'div',
35949                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
35950                         cn : [
35951                             {
35952                                 tag : 'div',
35953                                 cls : 'btn-group roo-document-viewer-download',
35954                                 cn : [
35955                                     {
35956                                         tag : 'button',
35957                                         cls : 'btn btn-default',
35958                                         html : '<i class="fa fa-download"></i>'
35959                                     }
35960                                 ]
35961                             },
35962                             {
35963                                 tag : 'div',
35964                                 cls : 'btn-group roo-document-viewer-trash',
35965                                 cn : [
35966                                     {
35967                                         tag : 'button',
35968                                         cls : 'btn btn-default',
35969                                         html : '<i class="fa fa-trash"></i>'
35970                                     }
35971                                 ]
35972                             }
35973                         ]
35974                     }
35975                 }
35976             ]
35977         };
35978         
35979         return cfg;
35980     },
35981     
35982     initEvents : function()
35983     {
35984         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
35985         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35986         
35987         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
35988         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35989         
35990         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
35991         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35992         
35993         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
35994         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
35995         
35996         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
35997         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
35998         
35999         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
36000         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
36001         
36002         this.bodyEl.on('click', this.onClick, this);
36003         this.downloadBtn.on('click', this.onDownload, this);
36004         this.trashBtn.on('click', this.onTrash, this);
36005         
36006         this.downloadBtn.hide();
36007         this.trashBtn.hide();
36008         
36009         if(this.showDownload){
36010             this.downloadBtn.show();
36011         }
36012         
36013         if(this.showTrash){
36014             this.trashBtn.show();
36015         }
36016         
36017         if(!this.showDownload && !this.showTrash) {
36018             this.footerEl.hide();
36019         }
36020         
36021     },
36022     
36023     initial : function()
36024     {
36025         this.fireEvent('initial', this);
36026         
36027     },
36028     
36029     onClick : function(e)
36030     {
36031         e.preventDefault();
36032         
36033         this.fireEvent('click', this);
36034     },
36035     
36036     onDownload : function(e)
36037     {
36038         e.preventDefault();
36039         
36040         this.fireEvent('download', this);
36041     },
36042     
36043     onTrash : function(e)
36044     {
36045         e.preventDefault();
36046         
36047         this.fireEvent('trash', this);
36048     }
36049     
36050 });
36051 /*
36052  * - LGPL
36053  *
36054  * FieldLabel
36055  * 
36056  */
36057
36058 /**
36059  * @class Roo.bootstrap.form.FieldLabel
36060  * @extends Roo.bootstrap.Component
36061  * Bootstrap FieldLabel class
36062  * @cfg {String} html contents of the element
36063  * @cfg {String} tag tag of the element default label
36064  * @cfg {String} cls class of the element
36065  * @cfg {String} target label target 
36066  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
36067  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
36068  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
36069  * @cfg {String} iconTooltip default "This field is required"
36070  * @cfg {String} indicatorpos (left|right) default left
36071  * 
36072  * @constructor
36073  * Create a new FieldLabel
36074  * @param {Object} config The config object
36075  */
36076
36077 Roo.bootstrap.form.FieldLabel = function(config){
36078     Roo.bootstrap.Element.superclass.constructor.call(this, config);
36079     
36080     this.addEvents({
36081             /**
36082              * @event invalid
36083              * Fires after the field has been marked as invalid.
36084              * @param {Roo.form.FieldLabel} this
36085              * @param {String} msg The validation message
36086              */
36087             invalid : true,
36088             /**
36089              * @event valid
36090              * Fires after the field has been validated with no errors.
36091              * @param {Roo.form.FieldLabel} this
36092              */
36093             valid : true
36094         });
36095 };
36096
36097 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
36098     
36099     tag: 'label',
36100     cls: '',
36101     html: '',
36102     target: '',
36103     allowBlank : true,
36104     invalidClass : 'has-warning',
36105     validClass : 'has-success',
36106     iconTooltip : 'This field is required',
36107     indicatorpos : 'left',
36108     
36109     getAutoCreate : function(){
36110         
36111         var cls = "";
36112         if (!this.allowBlank) {
36113             cls  = "visible";
36114         }
36115         
36116         var cfg = {
36117             tag : this.tag,
36118             cls : 'roo-bootstrap-field-label ' + this.cls,
36119             for : this.target,
36120             cn : [
36121                 {
36122                     tag : 'i',
36123                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
36124                     tooltip : this.iconTooltip
36125                 },
36126                 {
36127                     tag : 'span',
36128                     html : this.html
36129                 }
36130             ] 
36131         };
36132         
36133         if(this.indicatorpos == 'right'){
36134             var cfg = {
36135                 tag : this.tag,
36136                 cls : 'roo-bootstrap-field-label ' + this.cls,
36137                 for : this.target,
36138                 cn : [
36139                     {
36140                         tag : 'span',
36141                         html : this.html
36142                     },
36143                     {
36144                         tag : 'i',
36145                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
36146                         tooltip : this.iconTooltip
36147                     }
36148                 ] 
36149             };
36150         }
36151         
36152         return cfg;
36153     },
36154     
36155     initEvents: function() 
36156     {
36157         Roo.bootstrap.Element.superclass.initEvents.call(this);
36158         
36159         this.indicator = this.indicatorEl();
36160         
36161         if(this.indicator){
36162             this.indicator.removeClass('visible');
36163             this.indicator.addClass('invisible');
36164         }
36165         
36166         Roo.bootstrap.form.FieldLabel.register(this);
36167     },
36168     
36169     indicatorEl : function()
36170     {
36171         var indicator = this.el.select('i.roo-required-indicator',true).first();
36172         
36173         if(!indicator){
36174             return false;
36175         }
36176         
36177         return indicator;
36178         
36179     },
36180     
36181     /**
36182      * Mark this field as valid
36183      */
36184     markValid : function()
36185     {
36186         if(this.indicator){
36187             this.indicator.removeClass('visible');
36188             this.indicator.addClass('invisible');
36189         }
36190         if (Roo.bootstrap.version == 3) {
36191             this.el.removeClass(this.invalidClass);
36192             this.el.addClass(this.validClass);
36193         } else {
36194             this.el.removeClass('is-invalid');
36195             this.el.addClass('is-valid');
36196         }
36197         
36198         
36199         this.fireEvent('valid', this);
36200     },
36201     
36202     /**
36203      * Mark this field as invalid
36204      * @param {String} msg The validation message
36205      */
36206     markInvalid : function(msg)
36207     {
36208         if(this.indicator){
36209             this.indicator.removeClass('invisible');
36210             this.indicator.addClass('visible');
36211         }
36212           if (Roo.bootstrap.version == 3) {
36213             this.el.removeClass(this.validClass);
36214             this.el.addClass(this.invalidClass);
36215         } else {
36216             this.el.removeClass('is-valid');
36217             this.el.addClass('is-invalid');
36218         }
36219         
36220         
36221         this.fireEvent('invalid', this, msg);
36222     }
36223     
36224    
36225 });
36226
36227 Roo.apply(Roo.bootstrap.form.FieldLabel, {
36228     
36229     groups: {},
36230     
36231      /**
36232     * register a FieldLabel Group
36233     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
36234     */
36235     register : function(label)
36236     {
36237         if(this.groups.hasOwnProperty(label.target)){
36238             return;
36239         }
36240      
36241         this.groups[label.target] = label;
36242         
36243     },
36244     /**
36245     * fetch a FieldLabel Group based on the target
36246     * @param {string} target
36247     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
36248     */
36249     get: function(target) {
36250         if (typeof(this.groups[target]) == 'undefined') {
36251             return false;
36252         }
36253         
36254         return this.groups[target] ;
36255     }
36256 });
36257
36258  
36259
36260  /*
36261  * - LGPL
36262  *
36263  * page DateSplitField.
36264  * 
36265  */
36266
36267
36268 /**
36269  * @class Roo.bootstrap.form.DateSplitField
36270  * @extends Roo.bootstrap.Component
36271  * Bootstrap DateSplitField class
36272  * @cfg {string} fieldLabel - the label associated
36273  * @cfg {Number} labelWidth set the width of label (0-12)
36274  * @cfg {String} labelAlign (top|left)
36275  * @cfg {Boolean} dayAllowBlank (true|false) default false
36276  * @cfg {Boolean} monthAllowBlank (true|false) default false
36277  * @cfg {Boolean} yearAllowBlank (true|false) default false
36278  * @cfg {string} dayPlaceholder 
36279  * @cfg {string} monthPlaceholder
36280  * @cfg {string} yearPlaceholder
36281  * @cfg {string} dayFormat default 'd'
36282  * @cfg {string} monthFormat default 'm'
36283  * @cfg {string} yearFormat default 'Y'
36284  * @cfg {Number} labellg set the width of label (1-12)
36285  * @cfg {Number} labelmd set the width of label (1-12)
36286  * @cfg {Number} labelsm set the width of label (1-12)
36287  * @cfg {Number} labelxs set the width of label (1-12)
36288
36289  *     
36290  * @constructor
36291  * Create a new DateSplitField
36292  * @param {Object} config The config object
36293  */
36294
36295 Roo.bootstrap.form.DateSplitField = function(config){
36296     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
36297     
36298     this.addEvents({
36299         // raw events
36300          /**
36301          * @event years
36302          * getting the data of years
36303          * @param {Roo.bootstrap.form.DateSplitField} this
36304          * @param {Object} years
36305          */
36306         "years" : true,
36307         /**
36308          * @event days
36309          * getting the data of days
36310          * @param {Roo.bootstrap.form.DateSplitField} this
36311          * @param {Object} days
36312          */
36313         "days" : true,
36314         /**
36315          * @event invalid
36316          * Fires after the field has been marked as invalid.
36317          * @param {Roo.form.Field} this
36318          * @param {String} msg The validation message
36319          */
36320         invalid : true,
36321        /**
36322          * @event valid
36323          * Fires after the field has been validated with no errors.
36324          * @param {Roo.form.Field} this
36325          */
36326         valid : true
36327     });
36328 };
36329
36330 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
36331     
36332     fieldLabel : '',
36333     labelAlign : 'top',
36334     labelWidth : 3,
36335     dayAllowBlank : false,
36336     monthAllowBlank : false,
36337     yearAllowBlank : false,
36338     dayPlaceholder : '',
36339     monthPlaceholder : '',
36340     yearPlaceholder : '',
36341     dayFormat : 'd',
36342     monthFormat : 'm',
36343     yearFormat : 'Y',
36344     isFormField : true,
36345     labellg : 0,
36346     labelmd : 0,
36347     labelsm : 0,
36348     labelxs : 0,
36349     
36350     getAutoCreate : function()
36351     {
36352         var cfg = {
36353             tag : 'div',
36354             cls : 'row roo-date-split-field-group',
36355             cn : [
36356                 {
36357                     tag : 'input',
36358                     type : 'hidden',
36359                     cls : 'form-hidden-field roo-date-split-field-group-value',
36360                     name : this.name
36361                 }
36362             ]
36363         };
36364         
36365         var labelCls = 'col-md-12';
36366         var contentCls = 'col-md-4';
36367         
36368         if(this.fieldLabel){
36369             
36370             var label = {
36371                 tag : 'div',
36372                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
36373                 cn : [
36374                     {
36375                         tag : 'label',
36376                         html : this.fieldLabel
36377                     }
36378                 ]
36379             };
36380             
36381             if(this.labelAlign == 'left'){
36382             
36383                 if(this.labelWidth > 12){
36384                     label.style = "width: " + this.labelWidth + 'px';
36385                 }
36386
36387                 if(this.labelWidth < 13 && this.labelmd == 0){
36388                     this.labelmd = this.labelWidth;
36389                 }
36390
36391                 if(this.labellg > 0){
36392                     labelCls = ' col-lg-' + this.labellg;
36393                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
36394                 }
36395
36396                 if(this.labelmd > 0){
36397                     labelCls = ' col-md-' + this.labelmd;
36398                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
36399                 }
36400
36401                 if(this.labelsm > 0){
36402                     labelCls = ' col-sm-' + this.labelsm;
36403                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
36404                 }
36405
36406                 if(this.labelxs > 0){
36407                     labelCls = ' col-xs-' + this.labelxs;
36408                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
36409                 }
36410             }
36411             
36412             label.cls += ' ' + labelCls;
36413             
36414             cfg.cn.push(label);
36415         }
36416         
36417         Roo.each(['day', 'month', 'year'], function(t){
36418             cfg.cn.push({
36419                 tag : 'div',
36420                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
36421             });
36422         }, this);
36423         
36424         return cfg;
36425     },
36426     
36427     inputEl: function ()
36428     {
36429         return this.el.select('.roo-date-split-field-group-value', true).first();
36430     },
36431     
36432     onRender : function(ct, position) 
36433     {
36434         var _this = this;
36435         
36436         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
36437         
36438         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
36439         
36440         this.dayField = new Roo.bootstrap.form.ComboBox({
36441             allowBlank : this.dayAllowBlank,
36442             alwaysQuery : true,
36443             displayField : 'value',
36444             editable : false,
36445             fieldLabel : '',
36446             forceSelection : true,
36447             mode : 'local',
36448             placeholder : this.dayPlaceholder,
36449             selectOnFocus : true,
36450             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36451             triggerAction : 'all',
36452             typeAhead : true,
36453             valueField : 'value',
36454             store : new Roo.data.SimpleStore({
36455                 data : (function() {    
36456                     var days = [];
36457                     _this.fireEvent('days', _this, days);
36458                     return days;
36459                 })(),
36460                 fields : [ 'value' ]
36461             }),
36462             listeners : {
36463                 select : function (_self, record, index)
36464                 {
36465                     _this.setValue(_this.getValue());
36466                 }
36467             }
36468         });
36469
36470         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
36471         
36472         this.monthField = new Roo.bootstrap.form.MonthField({
36473             after : '<i class=\"fa fa-calendar\"></i>',
36474             allowBlank : this.monthAllowBlank,
36475             placeholder : this.monthPlaceholder,
36476             readOnly : true,
36477             listeners : {
36478                 render : function (_self)
36479                 {
36480                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
36481                         e.preventDefault();
36482                         _self.focus();
36483                     });
36484                 },
36485                 select : function (_self, oldvalue, newvalue)
36486                 {
36487                     _this.setValue(_this.getValue());
36488                 }
36489             }
36490         });
36491         
36492         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
36493         
36494         this.yearField = new Roo.bootstrap.form.ComboBox({
36495             allowBlank : this.yearAllowBlank,
36496             alwaysQuery : true,
36497             displayField : 'value',
36498             editable : false,
36499             fieldLabel : '',
36500             forceSelection : true,
36501             mode : 'local',
36502             placeholder : this.yearPlaceholder,
36503             selectOnFocus : true,
36504             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36505             triggerAction : 'all',
36506             typeAhead : true,
36507             valueField : 'value',
36508             store : new Roo.data.SimpleStore({
36509                 data : (function() {
36510                     var years = [];
36511                     _this.fireEvent('years', _this, years);
36512                     return years;
36513                 })(),
36514                 fields : [ 'value' ]
36515             }),
36516             listeners : {
36517                 select : function (_self, record, index)
36518                 {
36519                     _this.setValue(_this.getValue());
36520                 }
36521             }
36522         });
36523
36524         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
36525     },
36526     
36527     setValue : function(v, format)
36528     {
36529         this.inputEl.dom.value = v;
36530         
36531         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
36532         
36533         var d = Date.parseDate(v, f);
36534         
36535         if(!d){
36536             this.validate();
36537             return;
36538         }
36539         
36540         this.setDay(d.format(this.dayFormat));
36541         this.setMonth(d.format(this.monthFormat));
36542         this.setYear(d.format(this.yearFormat));
36543         
36544         this.validate();
36545         
36546         return;
36547     },
36548     
36549     setDay : function(v)
36550     {
36551         this.dayField.setValue(v);
36552         this.inputEl.dom.value = this.getValue();
36553         this.validate();
36554         return;
36555     },
36556     
36557     setMonth : function(v)
36558     {
36559         this.monthField.setValue(v, true);
36560         this.inputEl.dom.value = this.getValue();
36561         this.validate();
36562         return;
36563     },
36564     
36565     setYear : function(v)
36566     {
36567         this.yearField.setValue(v);
36568         this.inputEl.dom.value = this.getValue();
36569         this.validate();
36570         return;
36571     },
36572     
36573     getDay : function()
36574     {
36575         return this.dayField.getValue();
36576     },
36577     
36578     getMonth : function()
36579     {
36580         return this.monthField.getValue();
36581     },
36582     
36583     getYear : function()
36584     {
36585         return this.yearField.getValue();
36586     },
36587     
36588     getValue : function()
36589     {
36590         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
36591         
36592         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
36593         
36594         return date;
36595     },
36596     
36597     reset : function()
36598     {
36599         this.setDay('');
36600         this.setMonth('');
36601         this.setYear('');
36602         this.inputEl.dom.value = '';
36603         this.validate();
36604         return;
36605     },
36606     
36607     validate : function()
36608     {
36609         var d = this.dayField.validate();
36610         var m = this.monthField.validate();
36611         var y = this.yearField.validate();
36612         
36613         var valid = true;
36614         
36615         if(
36616                 (!this.dayAllowBlank && !d) ||
36617                 (!this.monthAllowBlank && !m) ||
36618                 (!this.yearAllowBlank && !y)
36619         ){
36620             valid = false;
36621         }
36622         
36623         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
36624             return valid;
36625         }
36626         
36627         if(valid){
36628             this.markValid();
36629             return valid;
36630         }
36631         
36632         this.markInvalid();
36633         
36634         return valid;
36635     },
36636     
36637     markValid : function()
36638     {
36639         
36640         var label = this.el.select('label', true).first();
36641         var icon = this.el.select('i.fa-star', true).first();
36642
36643         if(label && icon){
36644             icon.remove();
36645         }
36646         
36647         this.fireEvent('valid', this);
36648     },
36649     
36650      /**
36651      * Mark this field as invalid
36652      * @param {String} msg The validation message
36653      */
36654     markInvalid : function(msg)
36655     {
36656         
36657         var label = this.el.select('label', true).first();
36658         var icon = this.el.select('i.fa-star', true).first();
36659
36660         if(label && !icon){
36661             this.el.select('.roo-date-split-field-label', true).createChild({
36662                 tag : 'i',
36663                 cls : 'text-danger fa fa-lg fa-star',
36664                 tooltip : 'This field is required',
36665                 style : 'margin-right:5px;'
36666             }, label, true);
36667         }
36668         
36669         this.fireEvent('invalid', this, msg);
36670     },
36671     
36672     clearInvalid : function()
36673     {
36674         var label = this.el.select('label', true).first();
36675         var icon = this.el.select('i.fa-star', true).first();
36676
36677         if(label && icon){
36678             icon.remove();
36679         }
36680         
36681         this.fireEvent('valid', this);
36682     },
36683     
36684     getName: function()
36685     {
36686         return this.name;
36687     }
36688     
36689 });
36690
36691  
36692
36693 /**
36694  * @class Roo.bootstrap.LayoutMasonry
36695  * @extends Roo.bootstrap.Component
36696  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
36697  * Bootstrap Layout Masonry class
36698  *
36699  * This is based on 
36700  * http://masonry.desandro.com
36701  *
36702  * The idea is to render all the bricks based on vertical width...
36703  *
36704  * The original code extends 'outlayer' - we might need to use that....
36705
36706  * @constructor
36707  * Create a new Element
36708  * @param {Object} config The config object
36709  */
36710
36711 Roo.bootstrap.LayoutMasonry = function(config){
36712     
36713     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
36714     
36715     this.bricks = [];
36716     
36717     Roo.bootstrap.LayoutMasonry.register(this);
36718     
36719     this.addEvents({
36720         // raw events
36721         /**
36722          * @event layout
36723          * Fire after layout the items
36724          * @param {Roo.bootstrap.LayoutMasonry} this
36725          * @param {Roo.EventObject} e
36726          */
36727         "layout" : true
36728     });
36729     
36730 };
36731
36732 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
36733     
36734     /**
36735      * @cfg {Boolean} isLayoutInstant = no animation?
36736      */   
36737     isLayoutInstant : false, // needed?
36738    
36739     /**
36740      * @cfg {Number} boxWidth  width of the columns
36741      */   
36742     boxWidth : 450,
36743     
36744       /**
36745      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
36746      */   
36747     boxHeight : 0,
36748     
36749     /**
36750      * @cfg {Number} padWidth padding below box..
36751      */   
36752     padWidth : 10, 
36753     
36754     /**
36755      * @cfg {Number} gutter gutter width..
36756      */   
36757     gutter : 10,
36758     
36759      /**
36760      * @cfg {Number} maxCols maximum number of columns
36761      */   
36762     
36763     maxCols: 0,
36764     
36765     /**
36766      * @cfg {Boolean} isAutoInitial defalut true
36767      */   
36768     isAutoInitial : true, 
36769     
36770     containerWidth: 0,
36771     
36772     /**
36773      * @cfg {Boolean} isHorizontal defalut false
36774      */   
36775     isHorizontal : false, 
36776
36777     currentSize : null,
36778     
36779     tag: 'div',
36780     
36781     cls: '',
36782     
36783     bricks: null, //CompositeElement
36784     
36785     cols : 1,
36786     
36787     _isLayoutInited : false,
36788     
36789 //    isAlternative : false, // only use for vertical layout...
36790     
36791     /**
36792      * @cfg {Number} alternativePadWidth padding below box..
36793      */   
36794     alternativePadWidth : 50,
36795     
36796     selectedBrick : [],
36797     
36798     getAutoCreate : function(){
36799         
36800         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
36801         
36802         var cfg = {
36803             tag: this.tag,
36804             cls: 'blog-masonary-wrapper ' + this.cls,
36805             cn : {
36806                 cls : 'mas-boxes masonary'
36807             }
36808         };
36809         
36810         return cfg;
36811     },
36812     
36813     getChildContainer: function( )
36814     {
36815         if (this.boxesEl) {
36816             return this.boxesEl;
36817         }
36818         
36819         this.boxesEl = this.el.select('.mas-boxes').first();
36820         
36821         return this.boxesEl;
36822     },
36823     
36824     
36825     initEvents : function()
36826     {
36827         var _this = this;
36828         
36829         if(this.isAutoInitial){
36830             Roo.log('hook children rendered');
36831             this.on('childrenrendered', function() {
36832                 Roo.log('children rendered');
36833                 _this.initial();
36834             } ,this);
36835         }
36836     },
36837     
36838     initial : function()
36839     {
36840         this.selectedBrick = [];
36841         
36842         this.currentSize = this.el.getBox(true);
36843         
36844         Roo.EventManager.onWindowResize(this.resize, this); 
36845
36846         if(!this.isAutoInitial){
36847             this.layout();
36848             return;
36849         }
36850         
36851         this.layout();
36852         
36853         return;
36854         //this.layout.defer(500,this);
36855         
36856     },
36857     
36858     resize : function()
36859     {
36860         var cs = this.el.getBox(true);
36861         
36862         if (
36863                 this.currentSize.width == cs.width && 
36864                 this.currentSize.x == cs.x && 
36865                 this.currentSize.height == cs.height && 
36866                 this.currentSize.y == cs.y 
36867         ) {
36868             Roo.log("no change in with or X or Y");
36869             return;
36870         }
36871         
36872         this.currentSize = cs;
36873         
36874         this.layout();
36875         
36876     },
36877     
36878     layout : function()
36879     {   
36880         this._resetLayout();
36881         
36882         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
36883         
36884         this.layoutItems( isInstant );
36885       
36886         this._isLayoutInited = true;
36887         
36888         this.fireEvent('layout', this);
36889         
36890     },
36891     
36892     _resetLayout : function()
36893     {
36894         if(this.isHorizontal){
36895             this.horizontalMeasureColumns();
36896             return;
36897         }
36898         
36899         this.verticalMeasureColumns();
36900         
36901     },
36902     
36903     verticalMeasureColumns : function()
36904     {
36905         this.getContainerWidth();
36906         
36907 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
36908 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
36909 //            return;
36910 //        }
36911         
36912         var boxWidth = this.boxWidth + this.padWidth;
36913         
36914         if(this.containerWidth < this.boxWidth){
36915             boxWidth = this.containerWidth
36916         }
36917         
36918         var containerWidth = this.containerWidth;
36919         
36920         var cols = Math.floor(containerWidth / boxWidth);
36921         
36922         this.cols = Math.max( cols, 1 );
36923         
36924         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36925         
36926         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
36927         
36928         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
36929         
36930         this.colWidth = boxWidth + avail - this.padWidth;
36931         
36932         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
36933         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
36934     },
36935     
36936     horizontalMeasureColumns : function()
36937     {
36938         this.getContainerWidth();
36939         
36940         var boxWidth = this.boxWidth;
36941         
36942         if(this.containerWidth < boxWidth){
36943             boxWidth = this.containerWidth;
36944         }
36945         
36946         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
36947         
36948         this.el.setHeight(boxWidth);
36949         
36950     },
36951     
36952     getContainerWidth : function()
36953     {
36954         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36955     },
36956     
36957     layoutItems : function( isInstant )
36958     {
36959         Roo.log(this.bricks);
36960         
36961         var items = Roo.apply([], this.bricks);
36962         
36963         if(this.isHorizontal){
36964             this._horizontalLayoutItems( items , isInstant );
36965             return;
36966         }
36967         
36968 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
36969 //            this._verticalAlternativeLayoutItems( items , isInstant );
36970 //            return;
36971 //        }
36972         
36973         this._verticalLayoutItems( items , isInstant );
36974         
36975     },
36976     
36977     _verticalLayoutItems : function ( items , isInstant)
36978     {
36979         if ( !items || !items.length ) {
36980             return;
36981         }
36982         
36983         var standard = [
36984             ['xs', 'xs', 'xs', 'tall'],
36985             ['xs', 'xs', 'tall'],
36986             ['xs', 'xs', 'sm'],
36987             ['xs', 'xs', 'xs'],
36988             ['xs', 'tall'],
36989             ['xs', 'sm'],
36990             ['xs', 'xs'],
36991             ['xs'],
36992             
36993             ['sm', 'xs', 'xs'],
36994             ['sm', 'xs'],
36995             ['sm'],
36996             
36997             ['tall', 'xs', 'xs', 'xs'],
36998             ['tall', 'xs', 'xs'],
36999             ['tall', 'xs'],
37000             ['tall']
37001             
37002         ];
37003         
37004         var queue = [];
37005         
37006         var boxes = [];
37007         
37008         var box = [];
37009         
37010         Roo.each(items, function(item, k){
37011             
37012             switch (item.size) {
37013                 // these layouts take up a full box,
37014                 case 'md' :
37015                 case 'md-left' :
37016                 case 'md-right' :
37017                 case 'wide' :
37018                     
37019                     if(box.length){
37020                         boxes.push(box);
37021                         box = [];
37022                     }
37023                     
37024                     boxes.push([item]);
37025                     
37026                     break;
37027                     
37028                 case 'xs' :
37029                 case 'sm' :
37030                 case 'tall' :
37031                     
37032                     box.push(item);
37033                     
37034                     break;
37035                 default :
37036                     break;
37037                     
37038             }
37039             
37040         }, this);
37041         
37042         if(box.length){
37043             boxes.push(box);
37044             box = [];
37045         }
37046         
37047         var filterPattern = function(box, length)
37048         {
37049             if(!box.length){
37050                 return;
37051             }
37052             
37053             var match = false;
37054             
37055             var pattern = box.slice(0, length);
37056             
37057             var format = [];
37058             
37059             Roo.each(pattern, function(i){
37060                 format.push(i.size);
37061             }, this);
37062             
37063             Roo.each(standard, function(s){
37064                 
37065                 if(String(s) != String(format)){
37066                     return;
37067                 }
37068                 
37069                 match = true;
37070                 return false;
37071                 
37072             }, this);
37073             
37074             if(!match && length == 1){
37075                 return;
37076             }
37077             
37078             if(!match){
37079                 filterPattern(box, length - 1);
37080                 return;
37081             }
37082                 
37083             queue.push(pattern);
37084
37085             box = box.slice(length, box.length);
37086
37087             filterPattern(box, 4);
37088
37089             return;
37090             
37091         }
37092         
37093         Roo.each(boxes, function(box, k){
37094             
37095             if(!box.length){
37096                 return;
37097             }
37098             
37099             if(box.length == 1){
37100                 queue.push(box);
37101                 return;
37102             }
37103             
37104             filterPattern(box, 4);
37105             
37106         }, this);
37107         
37108         this._processVerticalLayoutQueue( queue, isInstant );
37109         
37110     },
37111     
37112 //    _verticalAlternativeLayoutItems : function( items , isInstant )
37113 //    {
37114 //        if ( !items || !items.length ) {
37115 //            return;
37116 //        }
37117 //
37118 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
37119 //        
37120 //    },
37121     
37122     _horizontalLayoutItems : function ( items , isInstant)
37123     {
37124         if ( !items || !items.length || items.length < 3) {
37125             return;
37126         }
37127         
37128         items.reverse();
37129         
37130         var eItems = items.slice(0, 3);
37131         
37132         items = items.slice(3, items.length);
37133         
37134         var standard = [
37135             ['xs', 'xs', 'xs', 'wide'],
37136             ['xs', 'xs', 'wide'],
37137             ['xs', 'xs', 'sm'],
37138             ['xs', 'xs', 'xs'],
37139             ['xs', 'wide'],
37140             ['xs', 'sm'],
37141             ['xs', 'xs'],
37142             ['xs'],
37143             
37144             ['sm', 'xs', 'xs'],
37145             ['sm', 'xs'],
37146             ['sm'],
37147             
37148             ['wide', 'xs', 'xs', 'xs'],
37149             ['wide', 'xs', 'xs'],
37150             ['wide', 'xs'],
37151             ['wide'],
37152             
37153             ['wide-thin']
37154         ];
37155         
37156         var queue = [];
37157         
37158         var boxes = [];
37159         
37160         var box = [];
37161         
37162         Roo.each(items, function(item, k){
37163             
37164             switch (item.size) {
37165                 case 'md' :
37166                 case 'md-left' :
37167                 case 'md-right' :
37168                 case 'tall' :
37169                     
37170                     if(box.length){
37171                         boxes.push(box);
37172                         box = [];
37173                     }
37174                     
37175                     boxes.push([item]);
37176                     
37177                     break;
37178                     
37179                 case 'xs' :
37180                 case 'sm' :
37181                 case 'wide' :
37182                 case 'wide-thin' :
37183                     
37184                     box.push(item);
37185                     
37186                     break;
37187                 default :
37188                     break;
37189                     
37190             }
37191             
37192         }, this);
37193         
37194         if(box.length){
37195             boxes.push(box);
37196             box = [];
37197         }
37198         
37199         var filterPattern = function(box, length)
37200         {
37201             if(!box.length){
37202                 return;
37203             }
37204             
37205             var match = false;
37206             
37207             var pattern = box.slice(0, length);
37208             
37209             var format = [];
37210             
37211             Roo.each(pattern, function(i){
37212                 format.push(i.size);
37213             }, this);
37214             
37215             Roo.each(standard, function(s){
37216                 
37217                 if(String(s) != String(format)){
37218                     return;
37219                 }
37220                 
37221                 match = true;
37222                 return false;
37223                 
37224             }, this);
37225             
37226             if(!match && length == 1){
37227                 return;
37228             }
37229             
37230             if(!match){
37231                 filterPattern(box, length - 1);
37232                 return;
37233             }
37234                 
37235             queue.push(pattern);
37236
37237             box = box.slice(length, box.length);
37238
37239             filterPattern(box, 4);
37240
37241             return;
37242             
37243         }
37244         
37245         Roo.each(boxes, function(box, k){
37246             
37247             if(!box.length){
37248                 return;
37249             }
37250             
37251             if(box.length == 1){
37252                 queue.push(box);
37253                 return;
37254             }
37255             
37256             filterPattern(box, 4);
37257             
37258         }, this);
37259         
37260         
37261         var prune = [];
37262         
37263         var pos = this.el.getBox(true);
37264         
37265         var minX = pos.x;
37266         
37267         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37268         
37269         var hit_end = false;
37270         
37271         Roo.each(queue, function(box){
37272             
37273             if(hit_end){
37274                 
37275                 Roo.each(box, function(b){
37276                 
37277                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
37278                     b.el.hide();
37279
37280                 }, this);
37281
37282                 return;
37283             }
37284             
37285             var mx = 0;
37286             
37287             Roo.each(box, function(b){
37288                 
37289                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
37290                 b.el.show();
37291
37292                 mx = Math.max(mx, b.x);
37293                 
37294             }, this);
37295             
37296             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
37297             
37298             if(maxX < minX){
37299                 
37300                 Roo.each(box, function(b){
37301                 
37302                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
37303                     b.el.hide();
37304                     
37305                 }, this);
37306                 
37307                 hit_end = true;
37308                 
37309                 return;
37310             }
37311             
37312             prune.push(box);
37313             
37314         }, this);
37315         
37316         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
37317     },
37318     
37319     /** Sets position of item in DOM
37320     * @param {Element} item
37321     * @param {Number} x - horizontal position
37322     * @param {Number} y - vertical position
37323     * @param {Boolean} isInstant - disables transitions
37324     */
37325     _processVerticalLayoutQueue : function( queue, isInstant )
37326     {
37327         var pos = this.el.getBox(true);
37328         var x = pos.x;
37329         var y = pos.y;
37330         var maxY = [];
37331         
37332         for (var i = 0; i < this.cols; i++){
37333             maxY[i] = pos.y;
37334         }
37335         
37336         Roo.each(queue, function(box, k){
37337             
37338             var col = k % this.cols;
37339             
37340             Roo.each(box, function(b,kk){
37341                 
37342                 b.el.position('absolute');
37343                 
37344                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37345                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37346                 
37347                 if(b.size == 'md-left' || b.size == 'md-right'){
37348                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37349                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37350                 }
37351                 
37352                 b.el.setWidth(width);
37353                 b.el.setHeight(height);
37354                 // iframe?
37355                 b.el.select('iframe',true).setSize(width,height);
37356                 
37357             }, this);
37358             
37359             for (var i = 0; i < this.cols; i++){
37360                 
37361                 if(maxY[i] < maxY[col]){
37362                     col = i;
37363                     continue;
37364                 }
37365                 
37366                 col = Math.min(col, i);
37367                 
37368             }
37369             
37370             x = pos.x + col * (this.colWidth + this.padWidth);
37371             
37372             y = maxY[col];
37373             
37374             var positions = [];
37375             
37376             switch (box.length){
37377                 case 1 :
37378                     positions = this.getVerticalOneBoxColPositions(x, y, box);
37379                     break;
37380                 case 2 :
37381                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
37382                     break;
37383                 case 3 :
37384                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
37385                     break;
37386                 case 4 :
37387                     positions = this.getVerticalFourBoxColPositions(x, y, box);
37388                     break;
37389                 default :
37390                     break;
37391             }
37392             
37393             Roo.each(box, function(b,kk){
37394                 
37395                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37396                 
37397                 var sz = b.el.getSize();
37398                 
37399                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
37400                 
37401             }, this);
37402             
37403         }, this);
37404         
37405         var mY = 0;
37406         
37407         for (var i = 0; i < this.cols; i++){
37408             mY = Math.max(mY, maxY[i]);
37409         }
37410         
37411         this.el.setHeight(mY - pos.y);
37412         
37413     },
37414     
37415 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
37416 //    {
37417 //        var pos = this.el.getBox(true);
37418 //        var x = pos.x;
37419 //        var y = pos.y;
37420 //        var maxX = pos.right;
37421 //        
37422 //        var maxHeight = 0;
37423 //        
37424 //        Roo.each(items, function(item, k){
37425 //            
37426 //            var c = k % 2;
37427 //            
37428 //            item.el.position('absolute');
37429 //                
37430 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
37431 //
37432 //            item.el.setWidth(width);
37433 //
37434 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
37435 //
37436 //            item.el.setHeight(height);
37437 //            
37438 //            if(c == 0){
37439 //                item.el.setXY([x, y], isInstant ? false : true);
37440 //            } else {
37441 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
37442 //            }
37443 //            
37444 //            y = y + height + this.alternativePadWidth;
37445 //            
37446 //            maxHeight = maxHeight + height + this.alternativePadWidth;
37447 //            
37448 //        }, this);
37449 //        
37450 //        this.el.setHeight(maxHeight);
37451 //        
37452 //    },
37453     
37454     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
37455     {
37456         var pos = this.el.getBox(true);
37457         
37458         var minX = pos.x;
37459         var minY = pos.y;
37460         
37461         var maxX = pos.right;
37462         
37463         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
37464         
37465         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37466         
37467         Roo.each(queue, function(box, k){
37468             
37469             Roo.each(box, function(b, kk){
37470                 
37471                 b.el.position('absolute');
37472                 
37473                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37474                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37475                 
37476                 if(b.size == 'md-left' || b.size == 'md-right'){
37477                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37478                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37479                 }
37480                 
37481                 b.el.setWidth(width);
37482                 b.el.setHeight(height);
37483                 
37484             }, this);
37485             
37486             if(!box.length){
37487                 return;
37488             }
37489             
37490             var positions = [];
37491             
37492             switch (box.length){
37493                 case 1 :
37494                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
37495                     break;
37496                 case 2 :
37497                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
37498                     break;
37499                 case 3 :
37500                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
37501                     break;
37502                 case 4 :
37503                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
37504                     break;
37505                 default :
37506                     break;
37507             }
37508             
37509             Roo.each(box, function(b,kk){
37510                 
37511                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37512                 
37513                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
37514                 
37515             }, this);
37516             
37517         }, this);
37518         
37519     },
37520     
37521     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
37522     {
37523         Roo.each(eItems, function(b,k){
37524             
37525             b.size = (k == 0) ? 'sm' : 'xs';
37526             b.x = (k == 0) ? 2 : 1;
37527             b.y = (k == 0) ? 2 : 1;
37528             
37529             b.el.position('absolute');
37530             
37531             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37532                 
37533             b.el.setWidth(width);
37534             
37535             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37536             
37537             b.el.setHeight(height);
37538             
37539         }, this);
37540
37541         var positions = [];
37542         
37543         positions.push({
37544             x : maxX - this.unitWidth * 2 - this.gutter,
37545             y : minY
37546         });
37547         
37548         positions.push({
37549             x : maxX - this.unitWidth,
37550             y : minY + (this.unitWidth + this.gutter) * 2
37551         });
37552         
37553         positions.push({
37554             x : maxX - this.unitWidth * 3 - this.gutter * 2,
37555             y : minY
37556         });
37557         
37558         Roo.each(eItems, function(b,k){
37559             
37560             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
37561
37562         }, this);
37563         
37564     },
37565     
37566     getVerticalOneBoxColPositions : function(x, y, box)
37567     {
37568         var pos = [];
37569         
37570         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
37571         
37572         if(box[0].size == 'md-left'){
37573             rand = 0;
37574         }
37575         
37576         if(box[0].size == 'md-right'){
37577             rand = 1;
37578         }
37579         
37580         pos.push({
37581             x : x + (this.unitWidth + this.gutter) * rand,
37582             y : y
37583         });
37584         
37585         return pos;
37586     },
37587     
37588     getVerticalTwoBoxColPositions : function(x, y, box)
37589     {
37590         var pos = [];
37591         
37592         if(box[0].size == 'xs'){
37593             
37594             pos.push({
37595                 x : x,
37596                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
37597             });
37598
37599             pos.push({
37600                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
37601                 y : y
37602             });
37603             
37604             return pos;
37605             
37606         }
37607         
37608         pos.push({
37609             x : x,
37610             y : y
37611         });
37612
37613         pos.push({
37614             x : x + (this.unitWidth + this.gutter) * 2,
37615             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
37616         });
37617         
37618         return pos;
37619         
37620     },
37621     
37622     getVerticalThreeBoxColPositions : function(x, y, box)
37623     {
37624         var pos = [];
37625         
37626         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
37627             
37628             pos.push({
37629                 x : x,
37630                 y : y
37631             });
37632
37633             pos.push({
37634                 x : x + (this.unitWidth + this.gutter) * 1,
37635                 y : y
37636             });
37637             
37638             pos.push({
37639                 x : x + (this.unitWidth + this.gutter) * 2,
37640                 y : y
37641             });
37642             
37643             return pos;
37644             
37645         }
37646         
37647         if(box[0].size == 'xs' && box[1].size == 'xs'){
37648             
37649             pos.push({
37650                 x : x,
37651                 y : y
37652             });
37653
37654             pos.push({
37655                 x : x,
37656                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
37657             });
37658             
37659             pos.push({
37660                 x : x + (this.unitWidth + this.gutter) * 1,
37661                 y : y
37662             });
37663             
37664             return pos;
37665             
37666         }
37667         
37668         pos.push({
37669             x : x,
37670             y : y
37671         });
37672
37673         pos.push({
37674             x : x + (this.unitWidth + this.gutter) * 2,
37675             y : y
37676         });
37677
37678         pos.push({
37679             x : x + (this.unitWidth + this.gutter) * 2,
37680             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
37681         });
37682             
37683         return pos;
37684         
37685     },
37686     
37687     getVerticalFourBoxColPositions : function(x, y, box)
37688     {
37689         var pos = [];
37690         
37691         if(box[0].size == 'xs'){
37692             
37693             pos.push({
37694                 x : x,
37695                 y : y
37696             });
37697
37698             pos.push({
37699                 x : x,
37700                 y : y + (this.unitHeight + this.gutter) * 1
37701             });
37702             
37703             pos.push({
37704                 x : x,
37705                 y : y + (this.unitHeight + this.gutter) * 2
37706             });
37707             
37708             pos.push({
37709                 x : x + (this.unitWidth + this.gutter) * 1,
37710                 y : y
37711             });
37712             
37713             return pos;
37714             
37715         }
37716         
37717         pos.push({
37718             x : x,
37719             y : y
37720         });
37721
37722         pos.push({
37723             x : x + (this.unitWidth + this.gutter) * 2,
37724             y : y
37725         });
37726
37727         pos.push({
37728             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
37729             y : y + (this.unitHeight + this.gutter) * 1
37730         });
37731
37732         pos.push({
37733             x : x + (this.unitWidth + this.gutter) * 2,
37734             y : y + (this.unitWidth + this.gutter) * 2
37735         });
37736
37737         return pos;
37738         
37739     },
37740     
37741     getHorizontalOneBoxColPositions : function(maxX, minY, box)
37742     {
37743         var pos = [];
37744         
37745         if(box[0].size == 'md-left'){
37746             pos.push({
37747                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37748                 y : minY
37749             });
37750             
37751             return pos;
37752         }
37753         
37754         if(box[0].size == 'md-right'){
37755             pos.push({
37756                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37757                 y : minY + (this.unitWidth + this.gutter) * 1
37758             });
37759             
37760             return pos;
37761         }
37762         
37763         var rand = Math.floor(Math.random() * (4 - box[0].y));
37764         
37765         pos.push({
37766             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37767             y : minY + (this.unitWidth + this.gutter) * rand
37768         });
37769         
37770         return pos;
37771         
37772     },
37773     
37774     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
37775     {
37776         var pos = [];
37777         
37778         if(box[0].size == 'xs'){
37779             
37780             pos.push({
37781                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37782                 y : minY
37783             });
37784
37785             pos.push({
37786                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37787                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
37788             });
37789             
37790             return pos;
37791             
37792         }
37793         
37794         pos.push({
37795             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37796             y : minY
37797         });
37798
37799         pos.push({
37800             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37801             y : minY + (this.unitWidth + this.gutter) * 2
37802         });
37803         
37804         return pos;
37805         
37806     },
37807     
37808     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
37809     {
37810         var pos = [];
37811         
37812         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
37813             
37814             pos.push({
37815                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37816                 y : minY
37817             });
37818
37819             pos.push({
37820                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37821                 y : minY + (this.unitWidth + this.gutter) * 1
37822             });
37823             
37824             pos.push({
37825                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37826                 y : minY + (this.unitWidth + this.gutter) * 2
37827             });
37828             
37829             return pos;
37830             
37831         }
37832         
37833         if(box[0].size == 'xs' && box[1].size == 'xs'){
37834             
37835             pos.push({
37836                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37837                 y : minY
37838             });
37839
37840             pos.push({
37841                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37842                 y : minY
37843             });
37844             
37845             pos.push({
37846                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37847                 y : minY + (this.unitWidth + this.gutter) * 1
37848             });
37849             
37850             return pos;
37851             
37852         }
37853         
37854         pos.push({
37855             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37856             y : minY
37857         });
37858
37859         pos.push({
37860             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37861             y : minY + (this.unitWidth + this.gutter) * 2
37862         });
37863
37864         pos.push({
37865             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37866             y : minY + (this.unitWidth + this.gutter) * 2
37867         });
37868             
37869         return pos;
37870         
37871     },
37872     
37873     getHorizontalFourBoxColPositions : function(maxX, minY, box)
37874     {
37875         var pos = [];
37876         
37877         if(box[0].size == 'xs'){
37878             
37879             pos.push({
37880                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37881                 y : minY
37882             });
37883
37884             pos.push({
37885                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37886                 y : minY
37887             });
37888             
37889             pos.push({
37890                 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),
37891                 y : minY
37892             });
37893             
37894             pos.push({
37895                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
37896                 y : minY + (this.unitWidth + this.gutter) * 1
37897             });
37898             
37899             return pos;
37900             
37901         }
37902         
37903         pos.push({
37904             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37905             y : minY
37906         });
37907         
37908         pos.push({
37909             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37910             y : minY + (this.unitWidth + this.gutter) * 2
37911         });
37912         
37913         pos.push({
37914             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37915             y : minY + (this.unitWidth + this.gutter) * 2
37916         });
37917         
37918         pos.push({
37919             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),
37920             y : minY + (this.unitWidth + this.gutter) * 2
37921         });
37922
37923         return pos;
37924         
37925     },
37926     
37927     /**
37928     * remove a Masonry Brick
37929     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
37930     */
37931     removeBrick : function(brick_id)
37932     {
37933         if (!brick_id) {
37934             return;
37935         }
37936         
37937         for (var i = 0; i<this.bricks.length; i++) {
37938             if (this.bricks[i].id == brick_id) {
37939                 this.bricks.splice(i,1);
37940                 this.el.dom.removeChild(Roo.get(brick_id).dom);
37941                 this.initial();
37942             }
37943         }
37944     },
37945     
37946     /**
37947     * adds a Masonry Brick
37948     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
37949     */
37950     addBrick : function(cfg)
37951     {
37952         var cn = new Roo.bootstrap.MasonryBrick(cfg);
37953         //this.register(cn);
37954         cn.parentId = this.id;
37955         cn.render(this.el);
37956         return cn;
37957     },
37958     
37959     /**
37960     * register a Masonry Brick
37961     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
37962     */
37963     
37964     register : function(brick)
37965     {
37966         this.bricks.push(brick);
37967         brick.masonryId = this.id;
37968     },
37969     
37970     /**
37971     * clear all the Masonry Brick
37972     */
37973     clearAll : function()
37974     {
37975         this.bricks = [];
37976         //this.getChildContainer().dom.innerHTML = "";
37977         this.el.dom.innerHTML = '';
37978     },
37979     
37980     getSelected : function()
37981     {
37982         if (!this.selectedBrick) {
37983             return false;
37984         }
37985         
37986         return this.selectedBrick;
37987     }
37988 });
37989
37990 Roo.apply(Roo.bootstrap.LayoutMasonry, {
37991     
37992     groups: {},
37993      /**
37994     * register a Masonry Layout
37995     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
37996     */
37997     
37998     register : function(layout)
37999     {
38000         this.groups[layout.id] = layout;
38001     },
38002     /**
38003     * fetch a  Masonry Layout based on the masonry layout ID
38004     * @param {string} the masonry layout to add
38005     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
38006     */
38007     
38008     get: function(layout_id) {
38009         if (typeof(this.groups[layout_id]) == 'undefined') {
38010             return false;
38011         }
38012         return this.groups[layout_id] ;
38013     }
38014     
38015     
38016     
38017 });
38018
38019  
38020
38021  /**
38022  *
38023  * This is based on 
38024  * http://masonry.desandro.com
38025  *
38026  * The idea is to render all the bricks based on vertical width...
38027  *
38028  * The original code extends 'outlayer' - we might need to use that....
38029  * 
38030  */
38031
38032
38033 /**
38034  * @class Roo.bootstrap.LayoutMasonryAuto
38035  * @extends Roo.bootstrap.Component
38036  * Bootstrap Layout Masonry class
38037  * 
38038  * @constructor
38039  * Create a new Element
38040  * @param {Object} config The config object
38041  */
38042
38043 Roo.bootstrap.LayoutMasonryAuto = function(config){
38044     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
38045 };
38046
38047 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
38048     
38049       /**
38050      * @cfg {Boolean} isFitWidth  - resize the width..
38051      */   
38052     isFitWidth : false,  // options..
38053     /**
38054      * @cfg {Boolean} isOriginLeft = left align?
38055      */   
38056     isOriginLeft : true,
38057     /**
38058      * @cfg {Boolean} isOriginTop = top align?
38059      */   
38060     isOriginTop : false,
38061     /**
38062      * @cfg {Boolean} isLayoutInstant = no animation?
38063      */   
38064     isLayoutInstant : false, // needed?
38065     /**
38066      * @cfg {Boolean} isResizingContainer = not sure if this is used..
38067      */   
38068     isResizingContainer : true,
38069     /**
38070      * @cfg {Number} columnWidth  width of the columns 
38071      */   
38072     
38073     columnWidth : 0,
38074     
38075     /**
38076      * @cfg {Number} maxCols maximum number of columns
38077      */   
38078     
38079     maxCols: 0,
38080     /**
38081      * @cfg {Number} padHeight padding below box..
38082      */   
38083     
38084     padHeight : 10, 
38085     
38086     /**
38087      * @cfg {Boolean} isAutoInitial defalut true
38088      */   
38089     
38090     isAutoInitial : true, 
38091     
38092     // private?
38093     gutter : 0,
38094     
38095     containerWidth: 0,
38096     initialColumnWidth : 0,
38097     currentSize : null,
38098     
38099     colYs : null, // array.
38100     maxY : 0,
38101     padWidth: 10,
38102     
38103     
38104     tag: 'div',
38105     cls: '',
38106     bricks: null, //CompositeElement
38107     cols : 0, // array?
38108     // element : null, // wrapped now this.el
38109     _isLayoutInited : null, 
38110     
38111     
38112     getAutoCreate : function(){
38113         
38114         var cfg = {
38115             tag: this.tag,
38116             cls: 'blog-masonary-wrapper ' + this.cls,
38117             cn : {
38118                 cls : 'mas-boxes masonary'
38119             }
38120         };
38121         
38122         return cfg;
38123     },
38124     
38125     getChildContainer: function( )
38126     {
38127         if (this.boxesEl) {
38128             return this.boxesEl;
38129         }
38130         
38131         this.boxesEl = this.el.select('.mas-boxes').first();
38132         
38133         return this.boxesEl;
38134     },
38135     
38136     
38137     initEvents : function()
38138     {
38139         var _this = this;
38140         
38141         if(this.isAutoInitial){
38142             Roo.log('hook children rendered');
38143             this.on('childrenrendered', function() {
38144                 Roo.log('children rendered');
38145                 _this.initial();
38146             } ,this);
38147         }
38148         
38149     },
38150     
38151     initial : function()
38152     {
38153         this.reloadItems();
38154
38155         this.currentSize = this.el.getBox(true);
38156
38157         /// was window resize... - let's see if this works..
38158         Roo.EventManager.onWindowResize(this.resize, this); 
38159
38160         if(!this.isAutoInitial){
38161             this.layout();
38162             return;
38163         }
38164         
38165         this.layout.defer(500,this);
38166     },
38167     
38168     reloadItems: function()
38169     {
38170         this.bricks = this.el.select('.masonry-brick', true);
38171         
38172         this.bricks.each(function(b) {
38173             //Roo.log(b.getSize());
38174             if (!b.attr('originalwidth')) {
38175                 b.attr('originalwidth',  b.getSize().width);
38176             }
38177             
38178         });
38179         
38180         Roo.log(this.bricks.elements.length);
38181     },
38182     
38183     resize : function()
38184     {
38185         Roo.log('resize');
38186         var cs = this.el.getBox(true);
38187         
38188         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
38189             Roo.log("no change in with or X");
38190             return;
38191         }
38192         this.currentSize = cs;
38193         this.layout();
38194     },
38195     
38196     layout : function()
38197     {
38198          Roo.log('layout');
38199         this._resetLayout();
38200         //this._manageStamps();
38201       
38202         // don't animate first layout
38203         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38204         this.layoutItems( isInstant );
38205       
38206         // flag for initalized
38207         this._isLayoutInited = true;
38208     },
38209     
38210     layoutItems : function( isInstant )
38211     {
38212         //var items = this._getItemsForLayout( this.items );
38213         // original code supports filtering layout items.. we just ignore it..
38214         
38215         this._layoutItems( this.bricks , isInstant );
38216       
38217         this._postLayout();
38218     },
38219     _layoutItems : function ( items , isInstant)
38220     {
38221        //this.fireEvent( 'layout', this, items );
38222     
38223
38224         if ( !items || !items.elements.length ) {
38225           // no items, emit event with empty array
38226             return;
38227         }
38228
38229         var queue = [];
38230         items.each(function(item) {
38231             Roo.log("layout item");
38232             Roo.log(item);
38233             // get x/y object from method
38234             var position = this._getItemLayoutPosition( item );
38235             // enqueue
38236             position.item = item;
38237             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
38238             queue.push( position );
38239         }, this);
38240       
38241         this._processLayoutQueue( queue );
38242     },
38243     /** Sets position of item in DOM
38244     * @param {Element} item
38245     * @param {Number} x - horizontal position
38246     * @param {Number} y - vertical position
38247     * @param {Boolean} isInstant - disables transitions
38248     */
38249     _processLayoutQueue : function( queue )
38250     {
38251         for ( var i=0, len = queue.length; i < len; i++ ) {
38252             var obj = queue[i];
38253             obj.item.position('absolute');
38254             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
38255         }
38256     },
38257       
38258     
38259     /**
38260     * Any logic you want to do after each layout,
38261     * i.e. size the container
38262     */
38263     _postLayout : function()
38264     {
38265         this.resizeContainer();
38266     },
38267     
38268     resizeContainer : function()
38269     {
38270         if ( !this.isResizingContainer ) {
38271             return;
38272         }
38273         var size = this._getContainerSize();
38274         if ( size ) {
38275             this.el.setSize(size.width,size.height);
38276             this.boxesEl.setSize(size.width,size.height);
38277         }
38278     },
38279     
38280     
38281     
38282     _resetLayout : function()
38283     {
38284         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
38285         this.colWidth = this.el.getWidth();
38286         //this.gutter = this.el.getWidth(); 
38287         
38288         this.measureColumns();
38289
38290         // reset column Y
38291         var i = this.cols;
38292         this.colYs = [];
38293         while (i--) {
38294             this.colYs.push( 0 );
38295         }
38296     
38297         this.maxY = 0;
38298     },
38299
38300     measureColumns : function()
38301     {
38302         this.getContainerWidth();
38303       // if columnWidth is 0, default to outerWidth of first item
38304         if ( !this.columnWidth ) {
38305             var firstItem = this.bricks.first();
38306             Roo.log(firstItem);
38307             this.columnWidth  = this.containerWidth;
38308             if (firstItem && firstItem.attr('originalwidth') ) {
38309                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
38310             }
38311             // columnWidth fall back to item of first element
38312             Roo.log("set column width?");
38313                         this.initialColumnWidth = this.columnWidth  ;
38314
38315             // if first elem has no width, default to size of container
38316             
38317         }
38318         
38319         
38320         if (this.initialColumnWidth) {
38321             this.columnWidth = this.initialColumnWidth;
38322         }
38323         
38324         
38325             
38326         // column width is fixed at the top - however if container width get's smaller we should
38327         // reduce it...
38328         
38329         // this bit calcs how man columns..
38330             
38331         var columnWidth = this.columnWidth += this.gutter;
38332       
38333         // calculate columns
38334         var containerWidth = this.containerWidth + this.gutter;
38335         
38336         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
38337         // fix rounding errors, typically with gutters
38338         var excess = columnWidth - containerWidth % columnWidth;
38339         
38340         
38341         // if overshoot is less than a pixel, round up, otherwise floor it
38342         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
38343         cols = Math[ mathMethod ]( cols );
38344         this.cols = Math.max( cols, 1 );
38345         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38346         
38347          // padding positioning..
38348         var totalColWidth = this.cols * this.columnWidth;
38349         var padavail = this.containerWidth - totalColWidth;
38350         // so for 2 columns - we need 3 'pads'
38351         
38352         var padNeeded = (1+this.cols) * this.padWidth;
38353         
38354         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
38355         
38356         this.columnWidth += padExtra
38357         //this.padWidth = Math.floor(padavail /  ( this.cols));
38358         
38359         // adjust colum width so that padding is fixed??
38360         
38361         // we have 3 columns ... total = width * 3
38362         // we have X left over... that should be used by 
38363         
38364         //if (this.expandC) {
38365             
38366         //}
38367         
38368         
38369         
38370     },
38371     
38372     getContainerWidth : function()
38373     {
38374        /* // container is parent if fit width
38375         var container = this.isFitWidth ? this.element.parentNode : this.element;
38376         // check that this.size and size are there
38377         // IE8 triggers resize on body size change, so they might not be
38378         
38379         var size = getSize( container );  //FIXME
38380         this.containerWidth = size && size.innerWidth; //FIXME
38381         */
38382          
38383         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
38384         
38385     },
38386     
38387     _getItemLayoutPosition : function( item )  // what is item?
38388     {
38389         // we resize the item to our columnWidth..
38390       
38391         item.setWidth(this.columnWidth);
38392         item.autoBoxAdjust  = false;
38393         
38394         var sz = item.getSize();
38395  
38396         // how many columns does this brick span
38397         var remainder = this.containerWidth % this.columnWidth;
38398         
38399         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
38400         // round if off by 1 pixel, otherwise use ceil
38401         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
38402         colSpan = Math.min( colSpan, this.cols );
38403         
38404         // normally this should be '1' as we dont' currently allow multi width columns..
38405         
38406         var colGroup = this._getColGroup( colSpan );
38407         // get the minimum Y value from the columns
38408         var minimumY = Math.min.apply( Math, colGroup );
38409         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
38410         
38411         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
38412          
38413         // position the brick
38414         var position = {
38415             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
38416             y: this.currentSize.y + minimumY + this.padHeight
38417         };
38418         
38419         Roo.log(position);
38420         // apply setHeight to necessary columns
38421         var setHeight = minimumY + sz.height + this.padHeight;
38422         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
38423         
38424         var setSpan = this.cols + 1 - colGroup.length;
38425         for ( var i = 0; i < setSpan; i++ ) {
38426           this.colYs[ shortColIndex + i ] = setHeight ;
38427         }
38428       
38429         return position;
38430     },
38431     
38432     /**
38433      * @param {Number} colSpan - number of columns the element spans
38434      * @returns {Array} colGroup
38435      */
38436     _getColGroup : function( colSpan )
38437     {
38438         if ( colSpan < 2 ) {
38439           // if brick spans only one column, use all the column Ys
38440           return this.colYs;
38441         }
38442       
38443         var colGroup = [];
38444         // how many different places could this brick fit horizontally
38445         var groupCount = this.cols + 1 - colSpan;
38446         // for each group potential horizontal position
38447         for ( var i = 0; i < groupCount; i++ ) {
38448           // make an array of colY values for that one group
38449           var groupColYs = this.colYs.slice( i, i + colSpan );
38450           // and get the max value of the array
38451           colGroup[i] = Math.max.apply( Math, groupColYs );
38452         }
38453         return colGroup;
38454     },
38455     /*
38456     _manageStamp : function( stamp )
38457     {
38458         var stampSize =  stamp.getSize();
38459         var offset = stamp.getBox();
38460         // get the columns that this stamp affects
38461         var firstX = this.isOriginLeft ? offset.x : offset.right;
38462         var lastX = firstX + stampSize.width;
38463         var firstCol = Math.floor( firstX / this.columnWidth );
38464         firstCol = Math.max( 0, firstCol );
38465         
38466         var lastCol = Math.floor( lastX / this.columnWidth );
38467         // lastCol should not go over if multiple of columnWidth #425
38468         lastCol -= lastX % this.columnWidth ? 0 : 1;
38469         lastCol = Math.min( this.cols - 1, lastCol );
38470         
38471         // set colYs to bottom of the stamp
38472         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
38473             stampSize.height;
38474             
38475         for ( var i = firstCol; i <= lastCol; i++ ) {
38476           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
38477         }
38478     },
38479     */
38480     
38481     _getContainerSize : function()
38482     {
38483         this.maxY = Math.max.apply( Math, this.colYs );
38484         var size = {
38485             height: this.maxY
38486         };
38487       
38488         if ( this.isFitWidth ) {
38489             size.width = this._getContainerFitWidth();
38490         }
38491       
38492         return size;
38493     },
38494     
38495     _getContainerFitWidth : function()
38496     {
38497         var unusedCols = 0;
38498         // count unused columns
38499         var i = this.cols;
38500         while ( --i ) {
38501           if ( this.colYs[i] !== 0 ) {
38502             break;
38503           }
38504           unusedCols++;
38505         }
38506         // fit container to columns that have been used
38507         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
38508     },
38509     
38510     needsResizeLayout : function()
38511     {
38512         var previousWidth = this.containerWidth;
38513         this.getContainerWidth();
38514         return previousWidth !== this.containerWidth;
38515     }
38516  
38517 });
38518
38519  
38520
38521  /*
38522  * - LGPL
38523  *
38524  * element
38525  * 
38526  */
38527
38528 /**
38529  * @class Roo.bootstrap.MasonryBrick
38530  * @extends Roo.bootstrap.Component
38531  * Bootstrap MasonryBrick class
38532  * 
38533  * @constructor
38534  * Create a new MasonryBrick
38535  * @param {Object} config The config object
38536  */
38537
38538 Roo.bootstrap.MasonryBrick = function(config){
38539     
38540     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
38541     
38542     Roo.bootstrap.MasonryBrick.register(this);
38543     
38544     this.addEvents({
38545         // raw events
38546         /**
38547          * @event click
38548          * When a MasonryBrick is clcik
38549          * @param {Roo.bootstrap.MasonryBrick} this
38550          * @param {Roo.EventObject} e
38551          */
38552         "click" : true
38553     });
38554 };
38555
38556 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
38557     
38558     /**
38559      * @cfg {String} title
38560      */   
38561     title : '',
38562     /**
38563      * @cfg {String} html
38564      */   
38565     html : '',
38566     /**
38567      * @cfg {String} bgimage
38568      */   
38569     bgimage : '',
38570     /**
38571      * @cfg {String} videourl
38572      */   
38573     videourl : '',
38574     /**
38575      * @cfg {String} cls
38576      */   
38577     cls : '',
38578     /**
38579      * @cfg {String} href
38580      */   
38581     href : '',
38582     /**
38583      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
38584      */   
38585     size : 'xs',
38586     
38587     /**
38588      * @cfg {String} placetitle (center|bottom)
38589      */   
38590     placetitle : '',
38591     
38592     /**
38593      * @cfg {Boolean} isFitContainer defalut true
38594      */   
38595     isFitContainer : true, 
38596     
38597     /**
38598      * @cfg {Boolean} preventDefault defalut false
38599      */   
38600     preventDefault : false, 
38601     
38602     /**
38603      * @cfg {Boolean} inverse defalut false
38604      */   
38605     maskInverse : false, 
38606     
38607     getAutoCreate : function()
38608     {
38609         if(!this.isFitContainer){
38610             return this.getSplitAutoCreate();
38611         }
38612         
38613         var cls = 'masonry-brick masonry-brick-full';
38614         
38615         if(this.href.length){
38616             cls += ' masonry-brick-link';
38617         }
38618         
38619         if(this.bgimage.length){
38620             cls += ' masonry-brick-image';
38621         }
38622         
38623         if(this.maskInverse){
38624             cls += ' mask-inverse';
38625         }
38626         
38627         if(!this.html.length && !this.maskInverse && !this.videourl.length){
38628             cls += ' enable-mask';
38629         }
38630         
38631         if(this.size){
38632             cls += ' masonry-' + this.size + '-brick';
38633         }
38634         
38635         if(this.placetitle.length){
38636             
38637             switch (this.placetitle) {
38638                 case 'center' :
38639                     cls += ' masonry-center-title';
38640                     break;
38641                 case 'bottom' :
38642                     cls += ' masonry-bottom-title';
38643                     break;
38644                 default:
38645                     break;
38646             }
38647             
38648         } else {
38649             if(!this.html.length && !this.bgimage.length){
38650                 cls += ' masonry-center-title';
38651             }
38652
38653             if(!this.html.length && this.bgimage.length){
38654                 cls += ' masonry-bottom-title';
38655             }
38656         }
38657         
38658         if(this.cls){
38659             cls += ' ' + this.cls;
38660         }
38661         
38662         var cfg = {
38663             tag: (this.href.length) ? 'a' : 'div',
38664             cls: cls,
38665             cn: [
38666                 {
38667                     tag: 'div',
38668                     cls: 'masonry-brick-mask'
38669                 },
38670                 {
38671                     tag: 'div',
38672                     cls: 'masonry-brick-paragraph',
38673                     cn: []
38674                 }
38675             ]
38676         };
38677         
38678         if(this.href.length){
38679             cfg.href = this.href;
38680         }
38681         
38682         var cn = cfg.cn[1].cn;
38683         
38684         if(this.title.length){
38685             cn.push({
38686                 tag: 'h4',
38687                 cls: 'masonry-brick-title',
38688                 html: this.title
38689             });
38690         }
38691         
38692         if(this.html.length){
38693             cn.push({
38694                 tag: 'p',
38695                 cls: 'masonry-brick-text',
38696                 html: this.html
38697             });
38698         }
38699         
38700         if (!this.title.length && !this.html.length) {
38701             cfg.cn[1].cls += ' hide';
38702         }
38703         
38704         if(this.bgimage.length){
38705             cfg.cn.push({
38706                 tag: 'img',
38707                 cls: 'masonry-brick-image-view',
38708                 src: this.bgimage
38709             });
38710         }
38711         
38712         if(this.videourl.length){
38713             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
38714             // youtube support only?
38715             cfg.cn.push({
38716                 tag: 'iframe',
38717                 cls: 'masonry-brick-image-view',
38718                 src: vurl,
38719                 frameborder : 0,
38720                 allowfullscreen : true
38721             });
38722         }
38723         
38724         return cfg;
38725         
38726     },
38727     
38728     getSplitAutoCreate : function()
38729     {
38730         var cls = 'masonry-brick masonry-brick-split';
38731         
38732         if(this.href.length){
38733             cls += ' masonry-brick-link';
38734         }
38735         
38736         if(this.bgimage.length){
38737             cls += ' masonry-brick-image';
38738         }
38739         
38740         if(this.size){
38741             cls += ' masonry-' + this.size + '-brick';
38742         }
38743         
38744         switch (this.placetitle) {
38745             case 'center' :
38746                 cls += ' masonry-center-title';
38747                 break;
38748             case 'bottom' :
38749                 cls += ' masonry-bottom-title';
38750                 break;
38751             default:
38752                 if(!this.bgimage.length){
38753                     cls += ' masonry-center-title';
38754                 }
38755
38756                 if(this.bgimage.length){
38757                     cls += ' masonry-bottom-title';
38758                 }
38759                 break;
38760         }
38761         
38762         if(this.cls){
38763             cls += ' ' + this.cls;
38764         }
38765         
38766         var cfg = {
38767             tag: (this.href.length) ? 'a' : 'div',
38768             cls: cls,
38769             cn: [
38770                 {
38771                     tag: 'div',
38772                     cls: 'masonry-brick-split-head',
38773                     cn: [
38774                         {
38775                             tag: 'div',
38776                             cls: 'masonry-brick-paragraph',
38777                             cn: []
38778                         }
38779                     ]
38780                 },
38781                 {
38782                     tag: 'div',
38783                     cls: 'masonry-brick-split-body',
38784                     cn: []
38785                 }
38786             ]
38787         };
38788         
38789         if(this.href.length){
38790             cfg.href = this.href;
38791         }
38792         
38793         if(this.title.length){
38794             cfg.cn[0].cn[0].cn.push({
38795                 tag: 'h4',
38796                 cls: 'masonry-brick-title',
38797                 html: this.title
38798             });
38799         }
38800         
38801         if(this.html.length){
38802             cfg.cn[1].cn.push({
38803                 tag: 'p',
38804                 cls: 'masonry-brick-text',
38805                 html: this.html
38806             });
38807         }
38808
38809         if(this.bgimage.length){
38810             cfg.cn[0].cn.push({
38811                 tag: 'img',
38812                 cls: 'masonry-brick-image-view',
38813                 src: this.bgimage
38814             });
38815         }
38816         
38817         if(this.videourl.length){
38818             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
38819             // youtube support only?
38820             cfg.cn[0].cn.cn.push({
38821                 tag: 'iframe',
38822                 cls: 'masonry-brick-image-view',
38823                 src: vurl,
38824                 frameborder : 0,
38825                 allowfullscreen : true
38826             });
38827         }
38828         
38829         return cfg;
38830     },
38831     
38832     initEvents: function() 
38833     {
38834         switch (this.size) {
38835             case 'xs' :
38836                 this.x = 1;
38837                 this.y = 1;
38838                 break;
38839             case 'sm' :
38840                 this.x = 2;
38841                 this.y = 2;
38842                 break;
38843             case 'md' :
38844             case 'md-left' :
38845             case 'md-right' :
38846                 this.x = 3;
38847                 this.y = 3;
38848                 break;
38849             case 'tall' :
38850                 this.x = 2;
38851                 this.y = 3;
38852                 break;
38853             case 'wide' :
38854                 this.x = 3;
38855                 this.y = 2;
38856                 break;
38857             case 'wide-thin' :
38858                 this.x = 3;
38859                 this.y = 1;
38860                 break;
38861                         
38862             default :
38863                 break;
38864         }
38865         
38866         if(Roo.isTouch){
38867             this.el.on('touchstart', this.onTouchStart, this);
38868             this.el.on('touchmove', this.onTouchMove, this);
38869             this.el.on('touchend', this.onTouchEnd, this);
38870             this.el.on('contextmenu', this.onContextMenu, this);
38871         } else {
38872             this.el.on('mouseenter'  ,this.enter, this);
38873             this.el.on('mouseleave', this.leave, this);
38874             this.el.on('click', this.onClick, this);
38875         }
38876         
38877         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
38878             this.parent().bricks.push(this);   
38879         }
38880         
38881     },
38882     
38883     onClick: function(e, el)
38884     {
38885         var time = this.endTimer - this.startTimer;
38886         // Roo.log(e.preventDefault());
38887         if(Roo.isTouch){
38888             if(time > 1000){
38889                 e.preventDefault();
38890                 return;
38891             }
38892         }
38893         
38894         if(!this.preventDefault){
38895             return;
38896         }
38897         
38898         e.preventDefault();
38899         
38900         if (this.activeClass != '') {
38901             this.selectBrick();
38902         }
38903         
38904         this.fireEvent('click', this, e);
38905     },
38906     
38907     enter: function(e, el)
38908     {
38909         e.preventDefault();
38910         
38911         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
38912             return;
38913         }
38914         
38915         if(this.bgimage.length && this.html.length){
38916             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
38917         }
38918     },
38919     
38920     leave: function(e, el)
38921     {
38922         e.preventDefault();
38923         
38924         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
38925             return;
38926         }
38927         
38928         if(this.bgimage.length && this.html.length){
38929             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
38930         }
38931     },
38932     
38933     onTouchStart: function(e, el)
38934     {
38935 //        e.preventDefault();
38936         
38937         this.touchmoved = false;
38938         
38939         if(!this.isFitContainer){
38940             return;
38941         }
38942         
38943         if(!this.bgimage.length || !this.html.length){
38944             return;
38945         }
38946         
38947         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
38948         
38949         this.timer = new Date().getTime();
38950         
38951     },
38952     
38953     onTouchMove: function(e, el)
38954     {
38955         this.touchmoved = true;
38956     },
38957     
38958     onContextMenu : function(e,el)
38959     {
38960         e.preventDefault();
38961         e.stopPropagation();
38962         return false;
38963     },
38964     
38965     onTouchEnd: function(e, el)
38966     {
38967 //        e.preventDefault();
38968         
38969         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
38970         
38971             this.leave(e,el);
38972             
38973             return;
38974         }
38975         
38976         if(!this.bgimage.length || !this.html.length){
38977             
38978             if(this.href.length){
38979                 window.location.href = this.href;
38980             }
38981             
38982             return;
38983         }
38984         
38985         if(!this.isFitContainer){
38986             return;
38987         }
38988         
38989         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
38990         
38991         window.location.href = this.href;
38992     },
38993     
38994     //selection on single brick only
38995     selectBrick : function() {
38996         
38997         if (!this.parentId) {
38998             return;
38999         }
39000         
39001         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
39002         var index = m.selectedBrick.indexOf(this.id);
39003         
39004         if ( index > -1) {
39005             m.selectedBrick.splice(index,1);
39006             this.el.removeClass(this.activeClass);
39007             return;
39008         }
39009         
39010         for(var i = 0; i < m.selectedBrick.length; i++) {
39011             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
39012             b.el.removeClass(b.activeClass);
39013         }
39014         
39015         m.selectedBrick = [];
39016         
39017         m.selectedBrick.push(this.id);
39018         this.el.addClass(this.activeClass);
39019         return;
39020     },
39021     
39022     isSelected : function(){
39023         return this.el.hasClass(this.activeClass);
39024         
39025     }
39026 });
39027
39028 Roo.apply(Roo.bootstrap.MasonryBrick, {
39029     
39030     //groups: {},
39031     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
39032      /**
39033     * register a Masonry Brick
39034     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39035     */
39036     
39037     register : function(brick)
39038     {
39039         //this.groups[brick.id] = brick;
39040         this.groups.add(brick.id, brick);
39041     },
39042     /**
39043     * fetch a  masonry brick based on the masonry brick ID
39044     * @param {string} the masonry brick to add
39045     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
39046     */
39047     
39048     get: function(brick_id) 
39049     {
39050         // if (typeof(this.groups[brick_id]) == 'undefined') {
39051         //     return false;
39052         // }
39053         // return this.groups[brick_id] ;
39054         
39055         if(this.groups.key(brick_id)) {
39056             return this.groups.key(brick_id);
39057         }
39058         
39059         return false;
39060     }
39061     
39062     
39063     
39064 });
39065
39066  /*
39067  * - LGPL
39068  *
39069  * element
39070  * 
39071  */
39072
39073 /**
39074  * @class Roo.bootstrap.Brick
39075  * @extends Roo.bootstrap.Component
39076  * Bootstrap Brick class
39077  * 
39078  * @constructor
39079  * Create a new Brick
39080  * @param {Object} config The config object
39081  */
39082
39083 Roo.bootstrap.Brick = function(config){
39084     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
39085     
39086     this.addEvents({
39087         // raw events
39088         /**
39089          * @event click
39090          * When a Brick is click
39091          * @param {Roo.bootstrap.Brick} this
39092          * @param {Roo.EventObject} e
39093          */
39094         "click" : true
39095     });
39096 };
39097
39098 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
39099     
39100     /**
39101      * @cfg {String} title
39102      */   
39103     title : '',
39104     /**
39105      * @cfg {String} html
39106      */   
39107     html : '',
39108     /**
39109      * @cfg {String} bgimage
39110      */   
39111     bgimage : '',
39112     /**
39113      * @cfg {String} cls
39114      */   
39115     cls : '',
39116     /**
39117      * @cfg {String} href
39118      */   
39119     href : '',
39120     /**
39121      * @cfg {String} video
39122      */   
39123     video : '',
39124     /**
39125      * @cfg {Boolean} square
39126      */   
39127     square : true,
39128     
39129     getAutoCreate : function()
39130     {
39131         var cls = 'roo-brick';
39132         
39133         if(this.href.length){
39134             cls += ' roo-brick-link';
39135         }
39136         
39137         if(this.bgimage.length){
39138             cls += ' roo-brick-image';
39139         }
39140         
39141         if(!this.html.length && !this.bgimage.length){
39142             cls += ' roo-brick-center-title';
39143         }
39144         
39145         if(!this.html.length && this.bgimage.length){
39146             cls += ' roo-brick-bottom-title';
39147         }
39148         
39149         if(this.cls){
39150             cls += ' ' + this.cls;
39151         }
39152         
39153         var cfg = {
39154             tag: (this.href.length) ? 'a' : 'div',
39155             cls: cls,
39156             cn: [
39157                 {
39158                     tag: 'div',
39159                     cls: 'roo-brick-paragraph',
39160                     cn: []
39161                 }
39162             ]
39163         };
39164         
39165         if(this.href.length){
39166             cfg.href = this.href;
39167         }
39168         
39169         var cn = cfg.cn[0].cn;
39170         
39171         if(this.title.length){
39172             cn.push({
39173                 tag: 'h4',
39174                 cls: 'roo-brick-title',
39175                 html: this.title
39176             });
39177         }
39178         
39179         if(this.html.length){
39180             cn.push({
39181                 tag: 'p',
39182                 cls: 'roo-brick-text',
39183                 html: this.html
39184             });
39185         } else {
39186             cn.cls += ' hide';
39187         }
39188         
39189         if(this.bgimage.length){
39190             cfg.cn.push({
39191                 tag: 'img',
39192                 cls: 'roo-brick-image-view',
39193                 src: this.bgimage
39194             });
39195         }
39196         
39197         return cfg;
39198     },
39199     
39200     initEvents: function() 
39201     {
39202         if(this.title.length || this.html.length){
39203             this.el.on('mouseenter'  ,this.enter, this);
39204             this.el.on('mouseleave', this.leave, this);
39205         }
39206         
39207         Roo.EventManager.onWindowResize(this.resize, this); 
39208         
39209         if(this.bgimage.length){
39210             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
39211             this.imageEl.on('load', this.onImageLoad, this);
39212             return;
39213         }
39214         
39215         this.resize();
39216     },
39217     
39218     onImageLoad : function()
39219     {
39220         this.resize();
39221     },
39222     
39223     resize : function()
39224     {
39225         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
39226         
39227         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
39228         
39229         if(this.bgimage.length){
39230             var image = this.el.select('.roo-brick-image-view', true).first();
39231             
39232             image.setWidth(paragraph.getWidth());
39233             
39234             if(this.square){
39235                 image.setHeight(paragraph.getWidth());
39236             }
39237             
39238             this.el.setHeight(image.getHeight());
39239             paragraph.setHeight(image.getHeight());
39240             
39241         }
39242         
39243     },
39244     
39245     enter: function(e, el)
39246     {
39247         e.preventDefault();
39248         
39249         if(this.bgimage.length){
39250             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
39251             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
39252         }
39253     },
39254     
39255     leave: function(e, el)
39256     {
39257         e.preventDefault();
39258         
39259         if(this.bgimage.length){
39260             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
39261             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
39262         }
39263     }
39264     
39265 });
39266
39267  
39268
39269  /*
39270  * - LGPL
39271  *
39272  * Number field 
39273  */
39274
39275 /**
39276  * @class Roo.bootstrap.form.NumberField
39277  * @extends Roo.bootstrap.form.Input
39278  * Bootstrap NumberField class
39279  * 
39280  * 
39281  * 
39282  * 
39283  * @constructor
39284  * Create a new NumberField
39285  * @param {Object} config The config object
39286  */
39287
39288 Roo.bootstrap.form.NumberField = function(config){
39289     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
39290 };
39291
39292 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
39293     
39294     /**
39295      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39296      */
39297     allowDecimals : true,
39298     /**
39299      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39300      */
39301     decimalSeparator : ".",
39302     /**
39303      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39304      */
39305     decimalPrecision : 2,
39306     /**
39307      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39308      */
39309     allowNegative : true,
39310     
39311     /**
39312      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
39313      */
39314     allowZero: true,
39315     /**
39316      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39317      */
39318     minValue : Number.NEGATIVE_INFINITY,
39319     /**
39320      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39321      */
39322     maxValue : Number.MAX_VALUE,
39323     /**
39324      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39325      */
39326     minText : "The minimum value for this field is {0}",
39327     /**
39328      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39329      */
39330     maxText : "The maximum value for this field is {0}",
39331     /**
39332      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39333      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39334      */
39335     nanText : "{0} is not a valid number",
39336     /**
39337      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
39338      */
39339     thousandsDelimiter : false,
39340     /**
39341      * @cfg {String} valueAlign alignment of value
39342      */
39343     valueAlign : "left",
39344
39345     getAutoCreate : function()
39346     {
39347         var hiddenInput = {
39348             tag: 'input',
39349             type: 'hidden',
39350             id: Roo.id(),
39351             cls: 'hidden-number-input'
39352         };
39353         
39354         if (this.name) {
39355             hiddenInput.name = this.name;
39356         }
39357         
39358         this.name = '';
39359         
39360         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
39361         
39362         this.name = hiddenInput.name;
39363         
39364         if(cfg.cn.length > 0) {
39365             cfg.cn.push(hiddenInput);
39366         }
39367         
39368         return cfg;
39369     },
39370
39371     // private
39372     initEvents : function()
39373     {   
39374         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
39375         
39376         var allowed = "0123456789";
39377         
39378         if(this.allowDecimals){
39379             allowed += this.decimalSeparator;
39380         }
39381         
39382         if(this.allowNegative){
39383             allowed += "-";
39384         }
39385         
39386         if(this.thousandsDelimiter) {
39387             allowed += ",";
39388         }
39389         
39390         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39391         
39392         var keyPress = function(e){
39393             
39394             var k = e.getKey();
39395             
39396             var c = e.getCharCode();
39397             
39398             if(
39399                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39400                     allowed.indexOf(String.fromCharCode(c)) === -1
39401             ){
39402                 e.stopEvent();
39403                 return;
39404             }
39405             
39406             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39407                 return;
39408             }
39409             
39410             if(allowed.indexOf(String.fromCharCode(c)) === -1){
39411                 e.stopEvent();
39412             }
39413         };
39414         
39415         this.el.on("keypress", keyPress, this);
39416     },
39417     
39418     validateValue : function(value)
39419     {
39420         
39421         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
39422             return false;
39423         }
39424         
39425         var num = this.parseValue(value);
39426         
39427         if(isNaN(num)){
39428             this.markInvalid(String.format(this.nanText, value));
39429             return false;
39430         }
39431         
39432         if(num < this.minValue){
39433             this.markInvalid(String.format(this.minText, this.minValue));
39434             return false;
39435         }
39436         
39437         if(num > this.maxValue){
39438             this.markInvalid(String.format(this.maxText, this.maxValue));
39439             return false;
39440         }
39441         
39442         return true;
39443     },
39444
39445     getValue : function()
39446     {
39447         var v = this.hiddenEl().getValue();
39448         
39449         return this.fixPrecision(this.parseValue(v));
39450     },
39451
39452     parseValue : function(value)
39453     {
39454         if(this.thousandsDelimiter) {
39455             value += "";
39456             r = new RegExp(",", "g");
39457             value = value.replace(r, "");
39458         }
39459         
39460         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
39461         return isNaN(value) ? '' : value;
39462     },
39463
39464     fixPrecision : function(value)
39465     {
39466         if(this.thousandsDelimiter) {
39467             value += "";
39468             r = new RegExp(",", "g");
39469             value = value.replace(r, "");
39470         }
39471         
39472         var nan = isNaN(value);
39473         
39474         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
39475             return nan ? '' : value;
39476         }
39477         return parseFloat(value).toFixed(this.decimalPrecision);
39478     },
39479
39480     setValue : function(v)
39481     {
39482         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
39483         
39484         this.value = v;
39485         
39486         if(this.rendered){
39487             
39488             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39489             
39490             this.inputEl().dom.value = (v == '') ? '' :
39491                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
39492             
39493             if(!this.allowZero && v === '0') {
39494                 this.hiddenEl().dom.value = '';
39495                 this.inputEl().dom.value = '';
39496             }
39497             
39498             this.validate();
39499         }
39500     },
39501
39502     decimalPrecisionFcn : function(v)
39503     {
39504         return Math.floor(v);
39505     },
39506
39507     beforeBlur : function()
39508     {
39509         var v = this.parseValue(this.getRawValue());
39510         
39511         if(v || v === 0 || v === ''){
39512             this.setValue(v);
39513         }
39514     },
39515     
39516     hiddenEl : function()
39517     {
39518         return this.el.select('input.hidden-number-input',true).first();
39519     }
39520     
39521 });
39522
39523  
39524
39525 /*
39526 * Licence: LGPL
39527 */
39528
39529 /**
39530  * @class Roo.bootstrap.DocumentSlider
39531  * @extends Roo.bootstrap.Component
39532  * Bootstrap DocumentSlider class
39533  * 
39534  * @constructor
39535  * Create a new DocumentViewer
39536  * @param {Object} config The config object
39537  */
39538
39539 Roo.bootstrap.DocumentSlider = function(config){
39540     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
39541     
39542     this.files = [];
39543     
39544     this.addEvents({
39545         /**
39546          * @event initial
39547          * Fire after initEvent
39548          * @param {Roo.bootstrap.DocumentSlider} this
39549          */
39550         "initial" : true,
39551         /**
39552          * @event update
39553          * Fire after update
39554          * @param {Roo.bootstrap.DocumentSlider} this
39555          */
39556         "update" : true,
39557         /**
39558          * @event click
39559          * Fire after click
39560          * @param {Roo.bootstrap.DocumentSlider} this
39561          */
39562         "click" : true
39563     });
39564 };
39565
39566 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
39567     
39568     files : false,
39569     
39570     indicator : 0,
39571     
39572     getAutoCreate : function()
39573     {
39574         var cfg = {
39575             tag : 'div',
39576             cls : 'roo-document-slider',
39577             cn : [
39578                 {
39579                     tag : 'div',
39580                     cls : 'roo-document-slider-header',
39581                     cn : [
39582                         {
39583                             tag : 'div',
39584                             cls : 'roo-document-slider-header-title'
39585                         }
39586                     ]
39587                 },
39588                 {
39589                     tag : 'div',
39590                     cls : 'roo-document-slider-body',
39591                     cn : [
39592                         {
39593                             tag : 'div',
39594                             cls : 'roo-document-slider-prev',
39595                             cn : [
39596                                 {
39597                                     tag : 'i',
39598                                     cls : 'fa fa-chevron-left'
39599                                 }
39600                             ]
39601                         },
39602                         {
39603                             tag : 'div',
39604                             cls : 'roo-document-slider-thumb',
39605                             cn : [
39606                                 {
39607                                     tag : 'img',
39608                                     cls : 'roo-document-slider-image'
39609                                 }
39610                             ]
39611                         },
39612                         {
39613                             tag : 'div',
39614                             cls : 'roo-document-slider-next',
39615                             cn : [
39616                                 {
39617                                     tag : 'i',
39618                                     cls : 'fa fa-chevron-right'
39619                                 }
39620                             ]
39621                         }
39622                     ]
39623                 }
39624             ]
39625         };
39626         
39627         return cfg;
39628     },
39629     
39630     initEvents : function()
39631     {
39632         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
39633         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
39634         
39635         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
39636         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
39637         
39638         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
39639         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
39640         
39641         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
39642         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
39643         
39644         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
39645         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
39646         
39647         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
39648         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39649         
39650         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
39651         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39652         
39653         this.thumbEl.on('click', this.onClick, this);
39654         
39655         this.prevIndicator.on('click', this.prev, this);
39656         
39657         this.nextIndicator.on('click', this.next, this);
39658         
39659     },
39660     
39661     initial : function()
39662     {
39663         if(this.files.length){
39664             this.indicator = 1;
39665             this.update()
39666         }
39667         
39668         this.fireEvent('initial', this);
39669     },
39670     
39671     update : function()
39672     {
39673         this.imageEl.attr('src', this.files[this.indicator - 1]);
39674         
39675         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
39676         
39677         this.prevIndicator.show();
39678         
39679         if(this.indicator == 1){
39680             this.prevIndicator.hide();
39681         }
39682         
39683         this.nextIndicator.show();
39684         
39685         if(this.indicator == this.files.length){
39686             this.nextIndicator.hide();
39687         }
39688         
39689         this.thumbEl.scrollTo('top');
39690         
39691         this.fireEvent('update', this);
39692     },
39693     
39694     onClick : function(e)
39695     {
39696         e.preventDefault();
39697         
39698         this.fireEvent('click', this);
39699     },
39700     
39701     prev : function(e)
39702     {
39703         e.preventDefault();
39704         
39705         this.indicator = Math.max(1, this.indicator - 1);
39706         
39707         this.update();
39708     },
39709     
39710     next : function(e)
39711     {
39712         e.preventDefault();
39713         
39714         this.indicator = Math.min(this.files.length, this.indicator + 1);
39715         
39716         this.update();
39717     }
39718 });
39719 /*
39720  * - LGPL
39721  *
39722  * RadioSet
39723  *
39724  *
39725  */
39726
39727 /**
39728  * @class Roo.bootstrap.form.RadioSet
39729  * @extends Roo.bootstrap.form.Input
39730  * @children Roo.bootstrap.form.Radio
39731  * Bootstrap RadioSet class
39732  * @cfg {String} indicatorpos (left|right) default left
39733  * @cfg {Boolean} inline (true|false) inline the element (default true)
39734  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
39735  * @constructor
39736  * Create a new RadioSet
39737  * @param {Object} config The config object
39738  */
39739
39740 Roo.bootstrap.form.RadioSet = function(config){
39741     
39742     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
39743     
39744     this.radioes = [];
39745     
39746     Roo.bootstrap.form.RadioSet.register(this);
39747     
39748     this.addEvents({
39749         /**
39750         * @event check
39751         * Fires when the element is checked or unchecked.
39752         * @param {Roo.bootstrap.form.RadioSet} this This radio
39753         * @param {Roo.bootstrap.form.Radio} item The checked item
39754         */
39755        check : true,
39756        /**
39757         * @event click
39758         * Fires when the element is click.
39759         * @param {Roo.bootstrap.form.RadioSet} this This radio set
39760         * @param {Roo.bootstrap.form.Radio} item The checked item
39761         * @param {Roo.EventObject} e The event object
39762         */
39763        click : true
39764     });
39765     
39766 };
39767
39768 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
39769
39770     radioes : false,
39771     
39772     inline : true,
39773     
39774     weight : '',
39775     
39776     indicatorpos : 'left',
39777     
39778     getAutoCreate : function()
39779     {
39780         var label = {
39781             tag : 'label',
39782             cls : 'roo-radio-set-label',
39783             cn : [
39784                 {
39785                     tag : 'span',
39786                     html : this.fieldLabel
39787                 }
39788             ]
39789         };
39790         if (Roo.bootstrap.version == 3) {
39791             
39792             
39793             if(this.indicatorpos == 'left'){
39794                 label.cn.unshift({
39795                     tag : 'i',
39796                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
39797                     tooltip : 'This field is required'
39798                 });
39799             } else {
39800                 label.cn.push({
39801                     tag : 'i',
39802                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
39803                     tooltip : 'This field is required'
39804                 });
39805             }
39806         }
39807         var items = {
39808             tag : 'div',
39809             cls : 'roo-radio-set-items'
39810         };
39811         
39812         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
39813         
39814         if (align === 'left' && this.fieldLabel.length) {
39815             
39816             items = {
39817                 cls : "roo-radio-set-right", 
39818                 cn: [
39819                     items
39820                 ]
39821             };
39822             
39823             if(this.labelWidth > 12){
39824                 label.style = "width: " + this.labelWidth + 'px';
39825             }
39826             
39827             if(this.labelWidth < 13 && this.labelmd == 0){
39828                 this.labelmd = this.labelWidth;
39829             }
39830             
39831             if(this.labellg > 0){
39832                 label.cls += ' col-lg-' + this.labellg;
39833                 items.cls += ' col-lg-' + (12 - this.labellg);
39834             }
39835             
39836             if(this.labelmd > 0){
39837                 label.cls += ' col-md-' + this.labelmd;
39838                 items.cls += ' col-md-' + (12 - this.labelmd);
39839             }
39840             
39841             if(this.labelsm > 0){
39842                 label.cls += ' col-sm-' + this.labelsm;
39843                 items.cls += ' col-sm-' + (12 - this.labelsm);
39844             }
39845             
39846             if(this.labelxs > 0){
39847                 label.cls += ' col-xs-' + this.labelxs;
39848                 items.cls += ' col-xs-' + (12 - this.labelxs);
39849             }
39850         }
39851         
39852         var cfg = {
39853             tag : 'div',
39854             cls : 'roo-radio-set',
39855             cn : [
39856                 {
39857                     tag : 'input',
39858                     cls : 'roo-radio-set-input',
39859                     type : 'hidden',
39860                     name : this.name,
39861                     value : this.value ? this.value :  ''
39862                 },
39863                 label,
39864                 items
39865             ]
39866         };
39867         
39868         if(this.weight.length){
39869             cfg.cls += ' roo-radio-' + this.weight;
39870         }
39871         
39872         if(this.inline) {
39873             cfg.cls += ' roo-radio-set-inline';
39874         }
39875         
39876         var settings=this;
39877         ['xs','sm','md','lg'].map(function(size){
39878             if (settings[size]) {
39879                 cfg.cls += ' col-' + size + '-' + settings[size];
39880             }
39881         });
39882         
39883         return cfg;
39884         
39885     },
39886
39887     initEvents : function()
39888     {
39889         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
39890         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
39891         
39892         if(!this.fieldLabel.length){
39893             this.labelEl.hide();
39894         }
39895         
39896         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
39897         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
39898         
39899         this.indicator = this.indicatorEl();
39900         
39901         if(this.indicator){
39902             this.indicator.addClass('invisible');
39903         }
39904         
39905         this.originalValue = this.getValue();
39906         
39907     },
39908     
39909     inputEl: function ()
39910     {
39911         return this.el.select('.roo-radio-set-input', true).first();
39912     },
39913     
39914     getChildContainer : function()
39915     {
39916         return this.itemsEl;
39917     },
39918     
39919     register : function(item)
39920     {
39921         this.radioes.push(item);
39922         
39923     },
39924     
39925     validate : function()
39926     {   
39927         if(this.getVisibilityEl().hasClass('hidden')){
39928             return true;
39929         }
39930         
39931         var valid = false;
39932         
39933         Roo.each(this.radioes, function(i){
39934             if(!i.checked){
39935                 return;
39936             }
39937             
39938             valid = true;
39939             return false;
39940         });
39941         
39942         if(this.allowBlank) {
39943             return true;
39944         }
39945         
39946         if(this.disabled || valid){
39947             this.markValid();
39948             return true;
39949         }
39950         
39951         this.markInvalid();
39952         return false;
39953         
39954     },
39955     
39956     markValid : function()
39957     {
39958         if(this.labelEl.isVisible(true) && this.indicatorEl()){
39959             this.indicatorEl().removeClass('visible');
39960             this.indicatorEl().addClass('invisible');
39961         }
39962         
39963         
39964         if (Roo.bootstrap.version == 3) {
39965             this.el.removeClass([this.invalidClass, this.validClass]);
39966             this.el.addClass(this.validClass);
39967         } else {
39968             this.el.removeClass(['is-invalid','is-valid']);
39969             this.el.addClass(['is-valid']);
39970         }
39971         this.fireEvent('valid', this);
39972     },
39973     
39974     markInvalid : function(msg)
39975     {
39976         if(this.allowBlank || this.disabled){
39977             return;
39978         }
39979         
39980         if(this.labelEl.isVisible(true) && this.indicatorEl()){
39981             this.indicatorEl().removeClass('invisible');
39982             this.indicatorEl().addClass('visible');
39983         }
39984         if (Roo.bootstrap.version == 3) {
39985             this.el.removeClass([this.invalidClass, this.validClass]);
39986             this.el.addClass(this.invalidClass);
39987         } else {
39988             this.el.removeClass(['is-invalid','is-valid']);
39989             this.el.addClass(['is-invalid']);
39990         }
39991         
39992         this.fireEvent('invalid', this, msg);
39993         
39994     },
39995     
39996     setValue : function(v, suppressEvent)
39997     {   
39998         if(this.value === v){
39999             return;
40000         }
40001         
40002         this.value = v;
40003         
40004         if(this.rendered){
40005             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40006         }
40007         
40008         Roo.each(this.radioes, function(i){
40009             i.checked = false;
40010             i.el.removeClass('checked');
40011         });
40012         
40013         Roo.each(this.radioes, function(i){
40014             
40015             if(i.value === v || i.value.toString() === v.toString()){
40016                 i.checked = true;
40017                 i.el.addClass('checked');
40018                 
40019                 if(suppressEvent !== true){
40020                     this.fireEvent('check', this, i);
40021                 }
40022                 
40023                 return false;
40024             }
40025             
40026         }, this);
40027         
40028         this.validate();
40029     },
40030     
40031     clearInvalid : function(){
40032         
40033         if(!this.el || this.preventMark){
40034             return;
40035         }
40036         
40037         this.el.removeClass([this.invalidClass]);
40038         
40039         this.fireEvent('valid', this);
40040     }
40041     
40042 });
40043
40044 Roo.apply(Roo.bootstrap.form.RadioSet, {
40045     
40046     groups: {},
40047     
40048     register : function(set)
40049     {
40050         this.groups[set.name] = set;
40051     },
40052     
40053     get: function(name) 
40054     {
40055         if (typeof(this.groups[name]) == 'undefined') {
40056             return false;
40057         }
40058         
40059         return this.groups[name] ;
40060     }
40061     
40062 });
40063 /*
40064  * Based on:
40065  * Ext JS Library 1.1.1
40066  * Copyright(c) 2006-2007, Ext JS, LLC.
40067  *
40068  * Originally Released Under LGPL - original licence link has changed is not relivant.
40069  *
40070  * Fork - LGPL
40071  * <script type="text/javascript">
40072  */
40073
40074
40075 /**
40076  * @class Roo.bootstrap.SplitBar
40077  * @extends Roo.util.Observable
40078  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
40079  * <br><br>
40080  * Usage:
40081  * <pre><code>
40082 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
40083                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
40084 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
40085 split.minSize = 100;
40086 split.maxSize = 600;
40087 split.animate = true;
40088 split.on('moved', splitterMoved);
40089 </code></pre>
40090  * @constructor
40091  * Create a new SplitBar
40092  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
40093  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
40094  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40095  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
40096                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
40097                         position of the SplitBar).
40098  */
40099 Roo.bootstrap.SplitBar = function(cfg){
40100     
40101     /** @private */
40102     
40103     //{
40104     //  dragElement : elm
40105     //  resizingElement: el,
40106         // optional..
40107     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
40108     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
40109         // existingProxy ???
40110     //}
40111     
40112     this.el = Roo.get(cfg.dragElement, true);
40113     this.el.dom.unselectable = "on";
40114     /** @private */
40115     this.resizingEl = Roo.get(cfg.resizingElement, true);
40116
40117     /**
40118      * @private
40119      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40120      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
40121      * @type Number
40122      */
40123     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
40124     
40125     /**
40126      * The minimum size of the resizing element. (Defaults to 0)
40127      * @type Number
40128      */
40129     this.minSize = 0;
40130     
40131     /**
40132      * The maximum size of the resizing element. (Defaults to 2000)
40133      * @type Number
40134      */
40135     this.maxSize = 2000;
40136     
40137     /**
40138      * Whether to animate the transition to the new size
40139      * @type Boolean
40140      */
40141     this.animate = false;
40142     
40143     /**
40144      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
40145      * @type Boolean
40146      */
40147     this.useShim = false;
40148     
40149     /** @private */
40150     this.shim = null;
40151     
40152     if(!cfg.existingProxy){
40153         /** @private */
40154         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
40155     }else{
40156         this.proxy = Roo.get(cfg.existingProxy).dom;
40157     }
40158     /** @private */
40159     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
40160     
40161     /** @private */
40162     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
40163     
40164     /** @private */
40165     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
40166     
40167     /** @private */
40168     this.dragSpecs = {};
40169     
40170     /**
40171      * @private The adapter to use to positon and resize elements
40172      */
40173     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40174     this.adapter.init(this);
40175     
40176     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40177         /** @private */
40178         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
40179         this.el.addClass("roo-splitbar-h");
40180     }else{
40181         /** @private */
40182         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
40183         this.el.addClass("roo-splitbar-v");
40184     }
40185     
40186     this.addEvents({
40187         /**
40188          * @event resize
40189          * Fires when the splitter is moved (alias for {@link #event-moved})
40190          * @param {Roo.bootstrap.SplitBar} this
40191          * @param {Number} newSize the new width or height
40192          */
40193         "resize" : true,
40194         /**
40195          * @event moved
40196          * Fires when the splitter is moved
40197          * @param {Roo.bootstrap.SplitBar} this
40198          * @param {Number} newSize the new width or height
40199          */
40200         "moved" : true,
40201         /**
40202          * @event beforeresize
40203          * Fires before the splitter is dragged
40204          * @param {Roo.bootstrap.SplitBar} this
40205          */
40206         "beforeresize" : true,
40207
40208         "beforeapply" : true
40209     });
40210
40211     Roo.util.Observable.call(this);
40212 };
40213
40214 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
40215     onStartProxyDrag : function(x, y){
40216         this.fireEvent("beforeresize", this);
40217         if(!this.overlay){
40218             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
40219             o.unselectable();
40220             o.enableDisplayMode("block");
40221             // all splitbars share the same overlay
40222             Roo.bootstrap.SplitBar.prototype.overlay = o;
40223         }
40224         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
40225         this.overlay.show();
40226         Roo.get(this.proxy).setDisplayed("block");
40227         var size = this.adapter.getElementSize(this);
40228         this.activeMinSize = this.getMinimumSize();;
40229         this.activeMaxSize = this.getMaximumSize();;
40230         var c1 = size - this.activeMinSize;
40231         var c2 = Math.max(this.activeMaxSize - size, 0);
40232         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40233             this.dd.resetConstraints();
40234             this.dd.setXConstraint(
40235                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
40236                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
40237             );
40238             this.dd.setYConstraint(0, 0);
40239         }else{
40240             this.dd.resetConstraints();
40241             this.dd.setXConstraint(0, 0);
40242             this.dd.setYConstraint(
40243                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
40244                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
40245             );
40246          }
40247         this.dragSpecs.startSize = size;
40248         this.dragSpecs.startPoint = [x, y];
40249         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
40250     },
40251     
40252     /** 
40253      * @private Called after the drag operation by the DDProxy
40254      */
40255     onEndProxyDrag : function(e){
40256         Roo.get(this.proxy).setDisplayed(false);
40257         var endPoint = Roo.lib.Event.getXY(e);
40258         if(this.overlay){
40259             this.overlay.hide();
40260         }
40261         var newSize;
40262         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40263             newSize = this.dragSpecs.startSize + 
40264                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
40265                     endPoint[0] - this.dragSpecs.startPoint[0] :
40266                     this.dragSpecs.startPoint[0] - endPoint[0]
40267                 );
40268         }else{
40269             newSize = this.dragSpecs.startSize + 
40270                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
40271                     endPoint[1] - this.dragSpecs.startPoint[1] :
40272                     this.dragSpecs.startPoint[1] - endPoint[1]
40273                 );
40274         }
40275         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
40276         if(newSize != this.dragSpecs.startSize){
40277             if(this.fireEvent('beforeapply', this, newSize) !== false){
40278                 this.adapter.setElementSize(this, newSize);
40279                 this.fireEvent("moved", this, newSize);
40280                 this.fireEvent("resize", this, newSize);
40281             }
40282         }
40283     },
40284     
40285     /**
40286      * Get the adapter this SplitBar uses
40287      * @return The adapter object
40288      */
40289     getAdapter : function(){
40290         return this.adapter;
40291     },
40292     
40293     /**
40294      * Set the adapter this SplitBar uses
40295      * @param {Object} adapter A SplitBar adapter object
40296      */
40297     setAdapter : function(adapter){
40298         this.adapter = adapter;
40299         this.adapter.init(this);
40300     },
40301     
40302     /**
40303      * Gets the minimum size for the resizing element
40304      * @return {Number} The minimum size
40305      */
40306     getMinimumSize : function(){
40307         return this.minSize;
40308     },
40309     
40310     /**
40311      * Sets the minimum size for the resizing element
40312      * @param {Number} minSize The minimum size
40313      */
40314     setMinimumSize : function(minSize){
40315         this.minSize = minSize;
40316     },
40317     
40318     /**
40319      * Gets the maximum size for the resizing element
40320      * @return {Number} The maximum size
40321      */
40322     getMaximumSize : function(){
40323         return this.maxSize;
40324     },
40325     
40326     /**
40327      * Sets the maximum size for the resizing element
40328      * @param {Number} maxSize The maximum size
40329      */
40330     setMaximumSize : function(maxSize){
40331         this.maxSize = maxSize;
40332     },
40333     
40334     /**
40335      * Sets the initialize size for the resizing element
40336      * @param {Number} size The initial size
40337      */
40338     setCurrentSize : function(size){
40339         var oldAnimate = this.animate;
40340         this.animate = false;
40341         this.adapter.setElementSize(this, size);
40342         this.animate = oldAnimate;
40343     },
40344     
40345     /**
40346      * Destroy this splitbar. 
40347      * @param {Boolean} removeEl True to remove the element
40348      */
40349     destroy : function(removeEl){
40350         if(this.shim){
40351             this.shim.remove();
40352         }
40353         this.dd.unreg();
40354         this.proxy.parentNode.removeChild(this.proxy);
40355         if(removeEl){
40356             this.el.remove();
40357         }
40358     }
40359 });
40360
40361 /**
40362  * @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.
40363  */
40364 Roo.bootstrap.SplitBar.createProxy = function(dir){
40365     var proxy = new Roo.Element(document.createElement("div"));
40366     proxy.unselectable();
40367     var cls = 'roo-splitbar-proxy';
40368     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
40369     document.body.appendChild(proxy.dom);
40370     return proxy.dom;
40371 };
40372
40373 /** 
40374  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
40375  * Default Adapter. It assumes the splitter and resizing element are not positioned
40376  * elements and only gets/sets the width of the element. Generally used for table based layouts.
40377  */
40378 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
40379 };
40380
40381 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
40382     // do nothing for now
40383     init : function(s){
40384     
40385     },
40386     /**
40387      * Called before drag operations to get the current size of the resizing element. 
40388      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40389      */
40390      getElementSize : function(s){
40391         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40392             return s.resizingEl.getWidth();
40393         }else{
40394             return s.resizingEl.getHeight();
40395         }
40396     },
40397     
40398     /**
40399      * Called after drag operations to set the size of the resizing element.
40400      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40401      * @param {Number} newSize The new size to set
40402      * @param {Function} onComplete A function to be invoked when resizing is complete
40403      */
40404     setElementSize : function(s, newSize, onComplete){
40405         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40406             if(!s.animate){
40407                 s.resizingEl.setWidth(newSize);
40408                 if(onComplete){
40409                     onComplete(s, newSize);
40410                 }
40411             }else{
40412                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
40413             }
40414         }else{
40415             
40416             if(!s.animate){
40417                 s.resizingEl.setHeight(newSize);
40418                 if(onComplete){
40419                     onComplete(s, newSize);
40420                 }
40421             }else{
40422                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
40423             }
40424         }
40425     }
40426 };
40427
40428 /** 
40429  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
40430  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
40431  * Adapter that  moves the splitter element to align with the resized sizing element. 
40432  * Used with an absolute positioned SplitBar.
40433  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
40434  * document.body, make sure you assign an id to the body element.
40435  */
40436 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
40437     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40438     this.container = Roo.get(container);
40439 };
40440
40441 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
40442     init : function(s){
40443         this.basic.init(s);
40444     },
40445     
40446     getElementSize : function(s){
40447         return this.basic.getElementSize(s);
40448     },
40449     
40450     setElementSize : function(s, newSize, onComplete){
40451         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
40452     },
40453     
40454     moveSplitter : function(s){
40455         var yes = Roo.bootstrap.SplitBar;
40456         switch(s.placement){
40457             case yes.LEFT:
40458                 s.el.setX(s.resizingEl.getRight());
40459                 break;
40460             case yes.RIGHT:
40461                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
40462                 break;
40463             case yes.TOP:
40464                 s.el.setY(s.resizingEl.getBottom());
40465                 break;
40466             case yes.BOTTOM:
40467                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
40468                 break;
40469         }
40470     }
40471 };
40472
40473 /**
40474  * Orientation constant - Create a vertical SplitBar
40475  * @static
40476  * @type Number
40477  */
40478 Roo.bootstrap.SplitBar.VERTICAL = 1;
40479
40480 /**
40481  * Orientation constant - Create a horizontal SplitBar
40482  * @static
40483  * @type Number
40484  */
40485 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
40486
40487 /**
40488  * Placement constant - The resizing element is to the left of the splitter element
40489  * @static
40490  * @type Number
40491  */
40492 Roo.bootstrap.SplitBar.LEFT = 1;
40493
40494 /**
40495  * Placement constant - The resizing element is to the right of the splitter element
40496  * @static
40497  * @type Number
40498  */
40499 Roo.bootstrap.SplitBar.RIGHT = 2;
40500
40501 /**
40502  * Placement constant - The resizing element is positioned above the splitter element
40503  * @static
40504  * @type Number
40505  */
40506 Roo.bootstrap.SplitBar.TOP = 3;
40507
40508 /**
40509  * Placement constant - The resizing element is positioned under splitter element
40510  * @static
40511  * @type Number
40512  */
40513 Roo.bootstrap.SplitBar.BOTTOM = 4;
40514 /*
40515  * Based on:
40516  * Ext JS Library 1.1.1
40517  * Copyright(c) 2006-2007, Ext JS, LLC.
40518  *
40519  * Originally Released Under LGPL - original licence link has changed is not relivant.
40520  *
40521  * Fork - LGPL
40522  * <script type="text/javascript">
40523  */
40524
40525 /**
40526  * @class Roo.bootstrap.layout.Manager
40527  * @extends Roo.bootstrap.Component
40528  * @abstract
40529  * Base class for layout managers.
40530  */
40531 Roo.bootstrap.layout.Manager = function(config)
40532 {
40533     this.monitorWindowResize = true; // do this before we apply configuration.
40534     
40535     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
40536
40537
40538
40539
40540
40541     /** false to disable window resize monitoring @type Boolean */
40542     
40543     this.regions = {};
40544     this.addEvents({
40545         /**
40546          * @event layout
40547          * Fires when a layout is performed.
40548          * @param {Roo.LayoutManager} this
40549          */
40550         "layout" : true,
40551         /**
40552          * @event regionresized
40553          * Fires when the user resizes a region.
40554          * @param {Roo.LayoutRegion} region The resized region
40555          * @param {Number} newSize The new size (width for east/west, height for north/south)
40556          */
40557         "regionresized" : true,
40558         /**
40559          * @event regioncollapsed
40560          * Fires when a region is collapsed.
40561          * @param {Roo.LayoutRegion} region The collapsed region
40562          */
40563         "regioncollapsed" : true,
40564         /**
40565          * @event regionexpanded
40566          * Fires when a region is expanded.
40567          * @param {Roo.LayoutRegion} region The expanded region
40568          */
40569         "regionexpanded" : true
40570     });
40571     this.updating = false;
40572
40573     if (config.el) {
40574         this.el = Roo.get(config.el);
40575         this.initEvents();
40576     }
40577
40578 };
40579
40580 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
40581
40582
40583     regions : null,
40584
40585     monitorWindowResize : true,
40586
40587
40588     updating : false,
40589
40590
40591     onRender : function(ct, position)
40592     {
40593         if(!this.el){
40594             this.el = Roo.get(ct);
40595             this.initEvents();
40596         }
40597         //this.fireEvent('render',this);
40598     },
40599
40600
40601     initEvents: function()
40602     {
40603
40604
40605         // ie scrollbar fix
40606         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
40607             document.body.scroll = "no";
40608         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
40609             this.el.position('relative');
40610         }
40611         this.id = this.el.id;
40612         this.el.addClass("roo-layout-container");
40613         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
40614         if(this.el.dom != document.body ) {
40615             this.el.on('resize', this.layout,this);
40616             this.el.on('show', this.layout,this);
40617         }
40618
40619     },
40620
40621     /**
40622      * Returns true if this layout is currently being updated
40623      * @return {Boolean}
40624      */
40625     isUpdating : function(){
40626         return this.updating;
40627     },
40628
40629     /**
40630      * Suspend the LayoutManager from doing auto-layouts while
40631      * making multiple add or remove calls
40632      */
40633     beginUpdate : function(){
40634         this.updating = true;
40635     },
40636
40637     /**
40638      * Restore auto-layouts and optionally disable the manager from performing a layout
40639      * @param {Boolean} noLayout true to disable a layout update
40640      */
40641     endUpdate : function(noLayout){
40642         this.updating = false;
40643         if(!noLayout){
40644             this.layout();
40645         }
40646     },
40647
40648     layout: function(){
40649         // abstract...
40650     },
40651
40652     onRegionResized : function(region, newSize){
40653         this.fireEvent("regionresized", region, newSize);
40654         this.layout();
40655     },
40656
40657     onRegionCollapsed : function(region){
40658         this.fireEvent("regioncollapsed", region);
40659     },
40660
40661     onRegionExpanded : function(region){
40662         this.fireEvent("regionexpanded", region);
40663     },
40664
40665     /**
40666      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
40667      * performs box-model adjustments.
40668      * @return {Object} The size as an object {width: (the width), height: (the height)}
40669      */
40670     getViewSize : function()
40671     {
40672         var size;
40673         if(this.el.dom != document.body){
40674             size = this.el.getSize();
40675         }else{
40676             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
40677         }
40678         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
40679         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40680         return size;
40681     },
40682
40683     /**
40684      * Returns the Element this layout is bound to.
40685      * @return {Roo.Element}
40686      */
40687     getEl : function(){
40688         return this.el;
40689     },
40690
40691     /**
40692      * Returns the specified region.
40693      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
40694      * @return {Roo.LayoutRegion}
40695      */
40696     getRegion : function(target){
40697         return this.regions[target.toLowerCase()];
40698     },
40699
40700     onWindowResize : function(){
40701         if(this.monitorWindowResize){
40702             this.layout();
40703         }
40704     }
40705 });
40706 /*
40707  * Based on:
40708  * Ext JS Library 1.1.1
40709  * Copyright(c) 2006-2007, Ext JS, LLC.
40710  *
40711  * Originally Released Under LGPL - original licence link has changed is not relivant.
40712  *
40713  * Fork - LGPL
40714  * <script type="text/javascript">
40715  */
40716 /**
40717  * @class Roo.bootstrap.layout.Border
40718  * @extends Roo.bootstrap.layout.Manager
40719  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
40720  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
40721  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
40722  * please see: examples/bootstrap/nested.html<br><br>
40723  
40724 <b>The container the layout is rendered into can be either the body element or any other element.
40725 If it is not the body element, the container needs to either be an absolute positioned element,
40726 or you will need to add "position:relative" to the css of the container.  You will also need to specify
40727 the container size if it is not the body element.</b>
40728
40729 * @constructor
40730 * Create a new Border
40731 * @param {Object} config Configuration options
40732  */
40733 Roo.bootstrap.layout.Border = function(config){
40734     config = config || {};
40735     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
40736     
40737     
40738     
40739     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
40740         if(config[region]){
40741             config[region].region = region;
40742             this.addRegion(config[region]);
40743         }
40744     },this);
40745     
40746 };
40747
40748 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
40749
40750 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
40751     
40752         /**
40753          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
40754          */
40755         /**
40756          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
40757          */
40758         /**
40759          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
40760          */
40761         /**
40762          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
40763          */
40764         /**
40765          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
40766          */
40767         
40768         
40769         
40770         
40771     parent : false, // this might point to a 'nest' or a ???
40772     
40773     /**
40774      * Creates and adds a new region if it doesn't already exist.
40775      * @param {String} target The target region key (north, south, east, west or center).
40776      * @param {Object} config The regions config object
40777      * @return {BorderLayoutRegion} The new region
40778      */
40779     addRegion : function(config)
40780     {
40781         if(!this.regions[config.region]){
40782             var r = this.factory(config);
40783             this.bindRegion(r);
40784         }
40785         return this.regions[config.region];
40786     },
40787
40788     // private (kinda)
40789     bindRegion : function(r){
40790         this.regions[r.config.region] = r;
40791         
40792         r.on("visibilitychange",    this.layout, this);
40793         r.on("paneladded",          this.layout, this);
40794         r.on("panelremoved",        this.layout, this);
40795         r.on("invalidated",         this.layout, this);
40796         r.on("resized",             this.onRegionResized, this);
40797         r.on("collapsed",           this.onRegionCollapsed, this);
40798         r.on("expanded",            this.onRegionExpanded, this);
40799     },
40800
40801     /**
40802      * Performs a layout update.
40803      */
40804     layout : function()
40805     {
40806         if(this.updating) {
40807             return;
40808         }
40809         
40810         // render all the rebions if they have not been done alreayd?
40811         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
40812             if(this.regions[region] && !this.regions[region].bodyEl){
40813                 this.regions[region].onRender(this.el)
40814             }
40815         },this);
40816         
40817         var size = this.getViewSize();
40818         var w = size.width;
40819         var h = size.height;
40820         var centerW = w;
40821         var centerH = h;
40822         var centerY = 0;
40823         var centerX = 0;
40824         //var x = 0, y = 0;
40825
40826         var rs = this.regions;
40827         var north = rs["north"];
40828         var south = rs["south"]; 
40829         var west = rs["west"];
40830         var east = rs["east"];
40831         var center = rs["center"];
40832         //if(this.hideOnLayout){ // not supported anymore
40833             //c.el.setStyle("display", "none");
40834         //}
40835         if(north && north.isVisible()){
40836             var b = north.getBox();
40837             var m = north.getMargins();
40838             b.width = w - (m.left+m.right);
40839             b.x = m.left;
40840             b.y = m.top;
40841             centerY = b.height + b.y + m.bottom;
40842             centerH -= centerY;
40843             north.updateBox(this.safeBox(b));
40844         }
40845         if(south && south.isVisible()){
40846             var b = south.getBox();
40847             var m = south.getMargins();
40848             b.width = w - (m.left+m.right);
40849             b.x = m.left;
40850             var totalHeight = (b.height + m.top + m.bottom);
40851             b.y = h - totalHeight + m.top;
40852             centerH -= totalHeight;
40853             south.updateBox(this.safeBox(b));
40854         }
40855         if(west && west.isVisible()){
40856             var b = west.getBox();
40857             var m = west.getMargins();
40858             b.height = centerH - (m.top+m.bottom);
40859             b.x = m.left;
40860             b.y = centerY + m.top;
40861             var totalWidth = (b.width + m.left + m.right);
40862             centerX += totalWidth;
40863             centerW -= totalWidth;
40864             west.updateBox(this.safeBox(b));
40865         }
40866         if(east && east.isVisible()){
40867             var b = east.getBox();
40868             var m = east.getMargins();
40869             b.height = centerH - (m.top+m.bottom);
40870             var totalWidth = (b.width + m.left + m.right);
40871             b.x = w - totalWidth + m.left;
40872             b.y = centerY + m.top;
40873             centerW -= totalWidth;
40874             east.updateBox(this.safeBox(b));
40875         }
40876         if(center){
40877             var m = center.getMargins();
40878             var centerBox = {
40879                 x: centerX + m.left,
40880                 y: centerY + m.top,
40881                 width: centerW - (m.left+m.right),
40882                 height: centerH - (m.top+m.bottom)
40883             };
40884             //if(this.hideOnLayout){
40885                 //center.el.setStyle("display", "block");
40886             //}
40887             center.updateBox(this.safeBox(centerBox));
40888         }
40889         this.el.repaint();
40890         this.fireEvent("layout", this);
40891     },
40892
40893     // private
40894     safeBox : function(box){
40895         box.width = Math.max(0, box.width);
40896         box.height = Math.max(0, box.height);
40897         return box;
40898     },
40899
40900     /**
40901      * Adds a ContentPanel (or subclass) to this layout.
40902      * @param {String} target The target region key (north, south, east, west or center).
40903      * @param {Roo.ContentPanel} panel The panel to add
40904      * @return {Roo.ContentPanel} The added panel
40905      */
40906     add : function(target, panel){
40907          
40908         target = target.toLowerCase();
40909         return this.regions[target].add(panel);
40910     },
40911
40912     /**
40913      * Remove a ContentPanel (or subclass) to this layout.
40914      * @param {String} target The target region key (north, south, east, west or center).
40915      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
40916      * @return {Roo.ContentPanel} The removed panel
40917      */
40918     remove : function(target, panel){
40919         target = target.toLowerCase();
40920         return this.regions[target].remove(panel);
40921     },
40922
40923     /**
40924      * Searches all regions for a panel with the specified id
40925      * @param {String} panelId
40926      * @return {Roo.ContentPanel} The panel or null if it wasn't found
40927      */
40928     findPanel : function(panelId){
40929         var rs = this.regions;
40930         for(var target in rs){
40931             if(typeof rs[target] != "function"){
40932                 var p = rs[target].getPanel(panelId);
40933                 if(p){
40934                     return p;
40935                 }
40936             }
40937         }
40938         return null;
40939     },
40940
40941     /**
40942      * Searches all regions for a panel with the specified id and activates (shows) it.
40943      * @param {String/ContentPanel} panelId The panels id or the panel itself
40944      * @return {Roo.ContentPanel} The shown panel or null
40945      */
40946     showPanel : function(panelId) {
40947       var rs = this.regions;
40948       for(var target in rs){
40949          var r = rs[target];
40950          if(typeof r != "function"){
40951             if(r.hasPanel(panelId)){
40952                return r.showPanel(panelId);
40953             }
40954          }
40955       }
40956       return null;
40957    },
40958
40959    /**
40960      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
40961      * @param {Roo.state.Provider} provider (optional) An alternate state provider
40962      */
40963    /*
40964     restoreState : function(provider){
40965         if(!provider){
40966             provider = Roo.state.Manager;
40967         }
40968         var sm = new Roo.LayoutStateManager();
40969         sm.init(this, provider);
40970     },
40971 */
40972  
40973  
40974     /**
40975      * Adds a xtype elements to the layout.
40976      * <pre><code>
40977
40978 layout.addxtype({
40979        xtype : 'ContentPanel',
40980        region: 'west',
40981        items: [ .... ]
40982    }
40983 );
40984
40985 layout.addxtype({
40986         xtype : 'NestedLayoutPanel',
40987         region: 'west',
40988         layout: {
40989            center: { },
40990            west: { }   
40991         },
40992         items : [ ... list of content panels or nested layout panels.. ]
40993    }
40994 );
40995 </code></pre>
40996      * @param {Object} cfg Xtype definition of item to add.
40997      */
40998     addxtype : function(cfg)
40999     {
41000         // basically accepts a pannel...
41001         // can accept a layout region..!?!?
41002         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
41003         
41004         
41005         // theory?  children can only be panels??
41006         
41007         //if (!cfg.xtype.match(/Panel$/)) {
41008         //    return false;
41009         //}
41010         var ret = false;
41011         
41012         if (typeof(cfg.region) == 'undefined') {
41013             Roo.log("Failed to add Panel, region was not set");
41014             Roo.log(cfg);
41015             return false;
41016         }
41017         var region = cfg.region;
41018         delete cfg.region;
41019         
41020           
41021         var xitems = [];
41022         if (cfg.items) {
41023             xitems = cfg.items;
41024             delete cfg.items;
41025         }
41026         var nb = false;
41027         
41028         if ( region == 'center') {
41029             Roo.log("Center: " + cfg.title);
41030         }
41031         
41032         
41033         switch(cfg.xtype) 
41034         {
41035             case 'Content':  // ContentPanel (el, cfg)
41036             case 'Scroll':  // ContentPanel (el, cfg)
41037             case 'View': 
41038                 cfg.autoCreate = cfg.autoCreate || true;
41039                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41040                 //} else {
41041                 //    var el = this.el.createChild();
41042                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
41043                 //}
41044                 
41045                 this.add(region, ret);
41046                 break;
41047             
41048             /*
41049             case 'TreePanel': // our new panel!
41050                 cfg.el = this.el.createChild();
41051                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41052                 this.add(region, ret);
41053                 break;
41054             */
41055             
41056             case 'Nest': 
41057                 // create a new Layout (which is  a Border Layout...
41058                 
41059                 var clayout = cfg.layout;
41060                 clayout.el  = this.el.createChild();
41061                 clayout.items   = clayout.items  || [];
41062                 
41063                 delete cfg.layout;
41064                 
41065                 // replace this exitems with the clayout ones..
41066                 xitems = clayout.items;
41067                  
41068                 // force background off if it's in center...
41069                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
41070                     cfg.background = false;
41071                 }
41072                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
41073                 
41074                 
41075                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41076                 //console.log('adding nested layout panel '  + cfg.toSource());
41077                 this.add(region, ret);
41078                 nb = {}; /// find first...
41079                 break;
41080             
41081             case 'Grid':
41082                 
41083                 // needs grid and region
41084                 
41085                 //var el = this.getRegion(region).el.createChild();
41086                 /*
41087                  *var el = this.el.createChild();
41088                 // create the grid first...
41089                 cfg.grid.container = el;
41090                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
41091                 */
41092                 
41093                 if (region == 'center' && this.active ) {
41094                     cfg.background = false;
41095                 }
41096                 
41097                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41098                 
41099                 this.add(region, ret);
41100                 /*
41101                 if (cfg.background) {
41102                     // render grid on panel activation (if panel background)
41103                     ret.on('activate', function(gp) {
41104                         if (!gp.grid.rendered) {
41105                     //        gp.grid.render(el);
41106                         }
41107                     });
41108                 } else {
41109                   //  cfg.grid.render(el);
41110                 }
41111                 */
41112                 break;
41113            
41114            
41115             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
41116                 // it was the old xcomponent building that caused this before.
41117                 // espeically if border is the top element in the tree.
41118                 ret = this;
41119                 break; 
41120                 
41121                     
41122                 
41123                 
41124                 
41125             default:
41126                 /*
41127                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
41128                     
41129                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41130                     this.add(region, ret);
41131                 } else {
41132                 */
41133                     Roo.log(cfg);
41134                     throw "Can not add '" + cfg.xtype + "' to Border";
41135                     return null;
41136              
41137                                 
41138              
41139         }
41140         this.beginUpdate();
41141         // add children..
41142         var region = '';
41143         var abn = {};
41144         Roo.each(xitems, function(i)  {
41145             region = nb && i.region ? i.region : false;
41146             
41147             var add = ret.addxtype(i);
41148            
41149             if (region) {
41150                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
41151                 if (!i.background) {
41152                     abn[region] = nb[region] ;
41153                 }
41154             }
41155             
41156         });
41157         this.endUpdate();
41158
41159         // make the last non-background panel active..
41160         //if (nb) { Roo.log(abn); }
41161         if (nb) {
41162             
41163             for(var r in abn) {
41164                 region = this.getRegion(r);
41165                 if (region) {
41166                     // tried using nb[r], but it does not work..
41167                      
41168                     region.showPanel(abn[r]);
41169                    
41170                 }
41171             }
41172         }
41173         return ret;
41174         
41175     },
41176     
41177     
41178 // private
41179     factory : function(cfg)
41180     {
41181         
41182         var validRegions = Roo.bootstrap.layout.Border.regions;
41183
41184         var target = cfg.region;
41185         cfg.mgr = this;
41186         
41187         var r = Roo.bootstrap.layout;
41188         Roo.log(target);
41189         switch(target){
41190             case "north":
41191                 return new r.North(cfg);
41192             case "south":
41193                 return new r.South(cfg);
41194             case "east":
41195                 return new r.East(cfg);
41196             case "west":
41197                 return new r.West(cfg);
41198             case "center":
41199                 return new r.Center(cfg);
41200         }
41201         throw 'Layout region "'+target+'" not supported.';
41202     }
41203     
41204     
41205 });
41206  /*
41207  * Based on:
41208  * Ext JS Library 1.1.1
41209  * Copyright(c) 2006-2007, Ext JS, LLC.
41210  *
41211  * Originally Released Under LGPL - original licence link has changed is not relivant.
41212  *
41213  * Fork - LGPL
41214  * <script type="text/javascript">
41215  */
41216  
41217 /**
41218  * @class Roo.bootstrap.layout.Basic
41219  * @extends Roo.util.Observable
41220  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
41221  * and does not have a titlebar, tabs or any other features. All it does is size and position 
41222  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
41223  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
41224  * @cfg {string}   region  the region that it inhabits..
41225  * @cfg {bool}   skipConfig skip config?
41226  * 
41227
41228  */
41229 Roo.bootstrap.layout.Basic = function(config){
41230     
41231     this.mgr = config.mgr;
41232     
41233     this.position = config.region;
41234     
41235     var skipConfig = config.skipConfig;
41236     
41237     this.events = {
41238         /**
41239          * @scope Roo.BasicLayoutRegion
41240          */
41241         
41242         /**
41243          * @event beforeremove
41244          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
41245          * @param {Roo.LayoutRegion} this
41246          * @param {Roo.ContentPanel} panel The panel
41247          * @param {Object} e The cancel event object
41248          */
41249         "beforeremove" : true,
41250         /**
41251          * @event invalidated
41252          * Fires when the layout for this region is changed.
41253          * @param {Roo.LayoutRegion} this
41254          */
41255         "invalidated" : true,
41256         /**
41257          * @event visibilitychange
41258          * Fires when this region is shown or hidden 
41259          * @param {Roo.LayoutRegion} this
41260          * @param {Boolean} visibility true or false
41261          */
41262         "visibilitychange" : true,
41263         /**
41264          * @event paneladded
41265          * Fires when a panel is added. 
41266          * @param {Roo.LayoutRegion} this
41267          * @param {Roo.ContentPanel} panel The panel
41268          */
41269         "paneladded" : true,
41270         /**
41271          * @event panelremoved
41272          * Fires when a panel is removed. 
41273          * @param {Roo.LayoutRegion} this
41274          * @param {Roo.ContentPanel} panel The panel
41275          */
41276         "panelremoved" : true,
41277         /**
41278          * @event beforecollapse
41279          * Fires when this region before collapse.
41280          * @param {Roo.LayoutRegion} this
41281          */
41282         "beforecollapse" : true,
41283         /**
41284          * @event collapsed
41285          * Fires when this region is collapsed.
41286          * @param {Roo.LayoutRegion} this
41287          */
41288         "collapsed" : true,
41289         /**
41290          * @event expanded
41291          * Fires when this region is expanded.
41292          * @param {Roo.LayoutRegion} this
41293          */
41294         "expanded" : true,
41295         /**
41296          * @event slideshow
41297          * Fires when this region is slid into view.
41298          * @param {Roo.LayoutRegion} this
41299          */
41300         "slideshow" : true,
41301         /**
41302          * @event slidehide
41303          * Fires when this region slides out of view. 
41304          * @param {Roo.LayoutRegion} this
41305          */
41306         "slidehide" : true,
41307         /**
41308          * @event panelactivated
41309          * Fires when a panel is activated. 
41310          * @param {Roo.LayoutRegion} this
41311          * @param {Roo.ContentPanel} panel The activated panel
41312          */
41313         "panelactivated" : true,
41314         /**
41315          * @event resized
41316          * Fires when the user resizes this region. 
41317          * @param {Roo.LayoutRegion} this
41318          * @param {Number} newSize The new size (width for east/west, height for north/south)
41319          */
41320         "resized" : true
41321     };
41322     /** A collection of panels in this region. @type Roo.util.MixedCollection */
41323     this.panels = new Roo.util.MixedCollection();
41324     this.panels.getKey = this.getPanelId.createDelegate(this);
41325     this.box = null;
41326     this.activePanel = null;
41327     // ensure listeners are added...
41328     
41329     if (config.listeners || config.events) {
41330         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
41331             listeners : config.listeners || {},
41332             events : config.events || {}
41333         });
41334     }
41335     
41336     if(skipConfig !== true){
41337         this.applyConfig(config);
41338     }
41339 };
41340
41341 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
41342 {
41343     getPanelId : function(p){
41344         return p.getId();
41345     },
41346     
41347     applyConfig : function(config){
41348         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41349         this.config = config;
41350         
41351     },
41352     
41353     /**
41354      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
41355      * the width, for horizontal (north, south) the height.
41356      * @param {Number} newSize The new width or height
41357      */
41358     resizeTo : function(newSize){
41359         var el = this.el ? this.el :
41360                  (this.activePanel ? this.activePanel.getEl() : null);
41361         if(el){
41362             switch(this.position){
41363                 case "east":
41364                 case "west":
41365                     el.setWidth(newSize);
41366                     this.fireEvent("resized", this, newSize);
41367                 break;
41368                 case "north":
41369                 case "south":
41370                     el.setHeight(newSize);
41371                     this.fireEvent("resized", this, newSize);
41372                 break;                
41373             }
41374         }
41375     },
41376     
41377     getBox : function(){
41378         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
41379     },
41380     
41381     getMargins : function(){
41382         return this.margins;
41383     },
41384     
41385     updateBox : function(box){
41386         this.box = box;
41387         var el = this.activePanel.getEl();
41388         el.dom.style.left = box.x + "px";
41389         el.dom.style.top = box.y + "px";
41390         this.activePanel.setSize(box.width, box.height);
41391     },
41392     
41393     /**
41394      * Returns the container element for this region.
41395      * @return {Roo.Element}
41396      */
41397     getEl : function(){
41398         return this.activePanel;
41399     },
41400     
41401     /**
41402      * Returns true if this region is currently visible.
41403      * @return {Boolean}
41404      */
41405     isVisible : function(){
41406         return this.activePanel ? true : false;
41407     },
41408     
41409     setActivePanel : function(panel){
41410         panel = this.getPanel(panel);
41411         if(this.activePanel && this.activePanel != panel){
41412             this.activePanel.setActiveState(false);
41413             this.activePanel.getEl().setLeftTop(-10000,-10000);
41414         }
41415         this.activePanel = panel;
41416         panel.setActiveState(true);
41417         if(this.box){
41418             panel.setSize(this.box.width, this.box.height);
41419         }
41420         this.fireEvent("panelactivated", this, panel);
41421         this.fireEvent("invalidated");
41422     },
41423     
41424     /**
41425      * Show the specified panel.
41426      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
41427      * @return {Roo.ContentPanel} The shown panel or null
41428      */
41429     showPanel : function(panel){
41430         panel = this.getPanel(panel);
41431         if(panel){
41432             this.setActivePanel(panel);
41433         }
41434         return panel;
41435     },
41436     
41437     /**
41438      * Get the active panel for this region.
41439      * @return {Roo.ContentPanel} The active panel or null
41440      */
41441     getActivePanel : function(){
41442         return this.activePanel;
41443     },
41444     
41445     /**
41446      * Add the passed ContentPanel(s)
41447      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
41448      * @return {Roo.ContentPanel} The panel added (if only one was added)
41449      */
41450     add : function(panel){
41451         if(arguments.length > 1){
41452             for(var i = 0, len = arguments.length; i < len; i++) {
41453                 this.add(arguments[i]);
41454             }
41455             return null;
41456         }
41457         if(this.hasPanel(panel)){
41458             this.showPanel(panel);
41459             return panel;
41460         }
41461         var el = panel.getEl();
41462         if(el.dom.parentNode != this.mgr.el.dom){
41463             this.mgr.el.dom.appendChild(el.dom);
41464         }
41465         if(panel.setRegion){
41466             panel.setRegion(this);
41467         }
41468         this.panels.add(panel);
41469         el.setStyle("position", "absolute");
41470         if(!panel.background){
41471             this.setActivePanel(panel);
41472             if(this.config.initialSize && this.panels.getCount()==1){
41473                 this.resizeTo(this.config.initialSize);
41474             }
41475         }
41476         this.fireEvent("paneladded", this, panel);
41477         return panel;
41478     },
41479     
41480     /**
41481      * Returns true if the panel is in this region.
41482      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41483      * @return {Boolean}
41484      */
41485     hasPanel : function(panel){
41486         if(typeof panel == "object"){ // must be panel obj
41487             panel = panel.getId();
41488         }
41489         return this.getPanel(panel) ? true : false;
41490     },
41491     
41492     /**
41493      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
41494      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41495      * @param {Boolean} preservePanel Overrides the config preservePanel option
41496      * @return {Roo.ContentPanel} The panel that was removed
41497      */
41498     remove : function(panel, preservePanel){
41499         panel = this.getPanel(panel);
41500         if(!panel){
41501             return null;
41502         }
41503         var e = {};
41504         this.fireEvent("beforeremove", this, panel, e);
41505         if(e.cancel === true){
41506             return null;
41507         }
41508         var panelId = panel.getId();
41509         this.panels.removeKey(panelId);
41510         return panel;
41511     },
41512     
41513     /**
41514      * Returns the panel specified or null if it's not in this region.
41515      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41516      * @return {Roo.ContentPanel}
41517      */
41518     getPanel : function(id){
41519         if(typeof id == "object"){ // must be panel obj
41520             return id;
41521         }
41522         return this.panels.get(id);
41523     },
41524     
41525     /**
41526      * Returns this regions position (north/south/east/west/center).
41527      * @return {String} 
41528      */
41529     getPosition: function(){
41530         return this.position;    
41531     }
41532 });/*
41533  * Based on:
41534  * Ext JS Library 1.1.1
41535  * Copyright(c) 2006-2007, Ext JS, LLC.
41536  *
41537  * Originally Released Under LGPL - original licence link has changed is not relivant.
41538  *
41539  * Fork - LGPL
41540  * <script type="text/javascript">
41541  */
41542  
41543 /**
41544  * @class Roo.bootstrap.layout.Region
41545  * @extends Roo.bootstrap.layout.Basic
41546  * This class represents a region in a layout manager.
41547  
41548  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
41549  * @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})
41550  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
41551  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
41552  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
41553  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
41554  * @cfg {String}    title           The title for the region (overrides panel titles)
41555  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
41556  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
41557  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
41558  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
41559  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
41560  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
41561  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
41562  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
41563  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
41564  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
41565
41566  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
41567  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
41568  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
41569  * @cfg {Number}    width           For East/West panels
41570  * @cfg {Number}    height          For North/South panels
41571  * @cfg {Boolean}   split           To show the splitter
41572  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
41573  * 
41574  * @cfg {string}   cls             Extra CSS classes to add to region
41575  * 
41576  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
41577  * @cfg {string}   region  the region that it inhabits..
41578  *
41579
41580  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
41581  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
41582
41583  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
41584  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
41585  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
41586  */
41587 Roo.bootstrap.layout.Region = function(config)
41588 {
41589     this.applyConfig(config);
41590
41591     var mgr = config.mgr;
41592     var pos = config.region;
41593     config.skipConfig = true;
41594     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
41595     
41596     if (mgr.el) {
41597         this.onRender(mgr.el);   
41598     }
41599      
41600     this.visible = true;
41601     this.collapsed = false;
41602     this.unrendered_panels = [];
41603 };
41604
41605 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
41606
41607     position: '', // set by wrapper (eg. north/south etc..)
41608     unrendered_panels : null,  // unrendered panels.
41609     
41610     tabPosition : false,
41611     
41612     mgr: false, // points to 'Border'
41613     
41614     
41615     createBody : function(){
41616         /** This region's body element 
41617         * @type Roo.Element */
41618         this.bodyEl = this.el.createChild({
41619                 tag: "div",
41620                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
41621         });
41622     },
41623
41624     onRender: function(ctr, pos)
41625     {
41626         var dh = Roo.DomHelper;
41627         /** This region's container element 
41628         * @type Roo.Element */
41629         this.el = dh.append(ctr.dom, {
41630                 tag: "div",
41631                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
41632             }, true);
41633         /** This region's title element 
41634         * @type Roo.Element */
41635     
41636         this.titleEl = dh.append(this.el.dom,  {
41637                 tag: "div",
41638                 unselectable: "on",
41639                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
41640                 children:[
41641                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
41642                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
41643                 ]
41644             }, true);
41645         
41646         this.titleEl.enableDisplayMode();
41647         /** This region's title text element 
41648         * @type HTMLElement */
41649         this.titleTextEl = this.titleEl.dom.firstChild;
41650         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
41651         /*
41652         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
41653         this.closeBtn.enableDisplayMode();
41654         this.closeBtn.on("click", this.closeClicked, this);
41655         this.closeBtn.hide();
41656     */
41657         this.createBody(this.config);
41658         if(this.config.hideWhenEmpty){
41659             this.hide();
41660             this.on("paneladded", this.validateVisibility, this);
41661             this.on("panelremoved", this.validateVisibility, this);
41662         }
41663         if(this.autoScroll){
41664             this.bodyEl.setStyle("overflow", "auto");
41665         }else{
41666             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
41667         }
41668         //if(c.titlebar !== false){
41669             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
41670                 this.titleEl.hide();
41671             }else{
41672                 this.titleEl.show();
41673                 if(this.config.title){
41674                     this.titleTextEl.innerHTML = this.config.title;
41675                 }
41676             }
41677         //}
41678         if(this.config.collapsed){
41679             this.collapse(true);
41680         }
41681         if(this.config.hidden){
41682             this.hide();
41683         }
41684         
41685         if (this.unrendered_panels && this.unrendered_panels.length) {
41686             for (var i =0;i< this.unrendered_panels.length; i++) {
41687                 this.add(this.unrendered_panels[i]);
41688             }
41689             this.unrendered_panels = null;
41690             
41691         }
41692         
41693     },
41694     
41695     applyConfig : function(c)
41696     {
41697         /*
41698          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
41699             var dh = Roo.DomHelper;
41700             if(c.titlebar !== false){
41701                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
41702                 this.collapseBtn.on("click", this.collapse, this);
41703                 this.collapseBtn.enableDisplayMode();
41704                 /*
41705                 if(c.showPin === true || this.showPin){
41706                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
41707                     this.stickBtn.enableDisplayMode();
41708                     this.stickBtn.on("click", this.expand, this);
41709                     this.stickBtn.hide();
41710                 }
41711                 
41712             }
41713             */
41714             /** This region's collapsed element
41715             * @type Roo.Element */
41716             /*
41717              *
41718             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
41719                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
41720             ]}, true);
41721             
41722             if(c.floatable !== false){
41723                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
41724                this.collapsedEl.on("click", this.collapseClick, this);
41725             }
41726
41727             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
41728                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
41729                    id: "message", unselectable: "on", style:{"float":"left"}});
41730                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
41731              }
41732             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
41733             this.expandBtn.on("click", this.expand, this);
41734             
41735         }
41736         
41737         if(this.collapseBtn){
41738             this.collapseBtn.setVisible(c.collapsible == true);
41739         }
41740         
41741         this.cmargins = c.cmargins || this.cmargins ||
41742                          (this.position == "west" || this.position == "east" ?
41743                              {top: 0, left: 2, right:2, bottom: 0} :
41744                              {top: 2, left: 0, right:0, bottom: 2});
41745         */
41746         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41747         
41748         
41749         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
41750         
41751         this.autoScroll = c.autoScroll || false;
41752         
41753         
41754        
41755         
41756         this.duration = c.duration || .30;
41757         this.slideDuration = c.slideDuration || .45;
41758         this.config = c;
41759        
41760     },
41761     /**
41762      * Returns true if this region is currently visible.
41763      * @return {Boolean}
41764      */
41765     isVisible : function(){
41766         return this.visible;
41767     },
41768
41769     /**
41770      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
41771      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
41772      */
41773     //setCollapsedTitle : function(title){
41774     //    title = title || "&#160;";
41775      //   if(this.collapsedTitleTextEl){
41776       //      this.collapsedTitleTextEl.innerHTML = title;
41777        // }
41778     //},
41779
41780     getBox : function(){
41781         var b;
41782       //  if(!this.collapsed){
41783             b = this.el.getBox(false, true);
41784        // }else{
41785           //  b = this.collapsedEl.getBox(false, true);
41786         //}
41787         return b;
41788     },
41789
41790     getMargins : function(){
41791         return this.margins;
41792         //return this.collapsed ? this.cmargins : this.margins;
41793     },
41794 /*
41795     highlight : function(){
41796         this.el.addClass("x-layout-panel-dragover");
41797     },
41798
41799     unhighlight : function(){
41800         this.el.removeClass("x-layout-panel-dragover");
41801     },
41802 */
41803     updateBox : function(box)
41804     {
41805         if (!this.bodyEl) {
41806             return; // not rendered yet..
41807         }
41808         
41809         this.box = box;
41810         if(!this.collapsed){
41811             this.el.dom.style.left = box.x + "px";
41812             this.el.dom.style.top = box.y + "px";
41813             this.updateBody(box.width, box.height);
41814         }else{
41815             this.collapsedEl.dom.style.left = box.x + "px";
41816             this.collapsedEl.dom.style.top = box.y + "px";
41817             this.collapsedEl.setSize(box.width, box.height);
41818         }
41819         if(this.tabs){
41820             this.tabs.autoSizeTabs();
41821         }
41822     },
41823
41824     updateBody : function(w, h)
41825     {
41826         if(w !== null){
41827             this.el.setWidth(w);
41828             w -= this.el.getBorderWidth("rl");
41829             if(this.config.adjustments){
41830                 w += this.config.adjustments[0];
41831             }
41832         }
41833         if(h !== null && h > 0){
41834             this.el.setHeight(h);
41835             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
41836             h -= this.el.getBorderWidth("tb");
41837             if(this.config.adjustments){
41838                 h += this.config.adjustments[1];
41839             }
41840             this.bodyEl.setHeight(h);
41841             if(this.tabs){
41842                 h = this.tabs.syncHeight(h);
41843             }
41844         }
41845         if(this.panelSize){
41846             w = w !== null ? w : this.panelSize.width;
41847             h = h !== null ? h : this.panelSize.height;
41848         }
41849         if(this.activePanel){
41850             var el = this.activePanel.getEl();
41851             w = w !== null ? w : el.getWidth();
41852             h = h !== null ? h : el.getHeight();
41853             this.panelSize = {width: w, height: h};
41854             this.activePanel.setSize(w, h);
41855         }
41856         if(Roo.isIE && this.tabs){
41857             this.tabs.el.repaint();
41858         }
41859     },
41860
41861     /**
41862      * Returns the container element for this region.
41863      * @return {Roo.Element}
41864      */
41865     getEl : function(){
41866         return this.el;
41867     },
41868
41869     /**
41870      * Hides this region.
41871      */
41872     hide : function(){
41873         //if(!this.collapsed){
41874             this.el.dom.style.left = "-2000px";
41875             this.el.hide();
41876         //}else{
41877          //   this.collapsedEl.dom.style.left = "-2000px";
41878          //   this.collapsedEl.hide();
41879        // }
41880         this.visible = false;
41881         this.fireEvent("visibilitychange", this, false);
41882     },
41883
41884     /**
41885      * Shows this region if it was previously hidden.
41886      */
41887     show : function(){
41888         //if(!this.collapsed){
41889             this.el.show();
41890         //}else{
41891         //    this.collapsedEl.show();
41892        // }
41893         this.visible = true;
41894         this.fireEvent("visibilitychange", this, true);
41895     },
41896 /*
41897     closeClicked : function(){
41898         if(this.activePanel){
41899             this.remove(this.activePanel);
41900         }
41901     },
41902
41903     collapseClick : function(e){
41904         if(this.isSlid){
41905            e.stopPropagation();
41906            this.slideIn();
41907         }else{
41908            e.stopPropagation();
41909            this.slideOut();
41910         }
41911     },
41912 */
41913     /**
41914      * Collapses this region.
41915      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
41916      */
41917     /*
41918     collapse : function(skipAnim, skipCheck = false){
41919         if(this.collapsed) {
41920             return;
41921         }
41922         
41923         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
41924             
41925             this.collapsed = true;
41926             if(this.split){
41927                 this.split.el.hide();
41928             }
41929             if(this.config.animate && skipAnim !== true){
41930                 this.fireEvent("invalidated", this);
41931                 this.animateCollapse();
41932             }else{
41933                 this.el.setLocation(-20000,-20000);
41934                 this.el.hide();
41935                 this.collapsedEl.show();
41936                 this.fireEvent("collapsed", this);
41937                 this.fireEvent("invalidated", this);
41938             }
41939         }
41940         
41941     },
41942 */
41943     animateCollapse : function(){
41944         // overridden
41945     },
41946
41947     /**
41948      * Expands this region if it was previously collapsed.
41949      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
41950      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
41951      */
41952     /*
41953     expand : function(e, skipAnim){
41954         if(e) {
41955             e.stopPropagation();
41956         }
41957         if(!this.collapsed || this.el.hasActiveFx()) {
41958             return;
41959         }
41960         if(this.isSlid){
41961             this.afterSlideIn();
41962             skipAnim = true;
41963         }
41964         this.collapsed = false;
41965         if(this.config.animate && skipAnim !== true){
41966             this.animateExpand();
41967         }else{
41968             this.el.show();
41969             if(this.split){
41970                 this.split.el.show();
41971             }
41972             this.collapsedEl.setLocation(-2000,-2000);
41973             this.collapsedEl.hide();
41974             this.fireEvent("invalidated", this);
41975             this.fireEvent("expanded", this);
41976         }
41977     },
41978 */
41979     animateExpand : function(){
41980         // overridden
41981     },
41982
41983     initTabs : function()
41984     {
41985         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
41986         
41987         var ts = new Roo.bootstrap.panel.Tabs({
41988             el: this.bodyEl.dom,
41989             region : this,
41990             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
41991             disableTooltips: this.config.disableTabTips,
41992             toolbar : this.config.toolbar
41993         });
41994         
41995         if(this.config.hideTabs){
41996             ts.stripWrap.setDisplayed(false);
41997         }
41998         this.tabs = ts;
41999         ts.resizeTabs = this.config.resizeTabs === true;
42000         ts.minTabWidth = this.config.minTabWidth || 40;
42001         ts.maxTabWidth = this.config.maxTabWidth || 250;
42002         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
42003         ts.monitorResize = false;
42004         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
42005         ts.bodyEl.addClass('roo-layout-tabs-body');
42006         this.panels.each(this.initPanelAsTab, this);
42007     },
42008
42009     initPanelAsTab : function(panel){
42010         var ti = this.tabs.addTab(
42011             panel.getEl().id,
42012             panel.getTitle(),
42013             null,
42014             this.config.closeOnTab && panel.isClosable(),
42015             panel.tpl
42016         );
42017         if(panel.tabTip !== undefined){
42018             ti.setTooltip(panel.tabTip);
42019         }
42020         ti.on("activate", function(){
42021               this.setActivePanel(panel);
42022         }, this);
42023         
42024         if(this.config.closeOnTab){
42025             ti.on("beforeclose", function(t, e){
42026                 e.cancel = true;
42027                 this.remove(panel);
42028             }, this);
42029         }
42030         
42031         panel.tabItem = ti;
42032         
42033         return ti;
42034     },
42035
42036     updatePanelTitle : function(panel, title)
42037     {
42038         if(this.activePanel == panel){
42039             this.updateTitle(title);
42040         }
42041         if(this.tabs){
42042             var ti = this.tabs.getTab(panel.getEl().id);
42043             ti.setText(title);
42044             if(panel.tabTip !== undefined){
42045                 ti.setTooltip(panel.tabTip);
42046             }
42047         }
42048     },
42049
42050     updateTitle : function(title){
42051         if(this.titleTextEl && !this.config.title){
42052             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
42053         }
42054     },
42055
42056     setActivePanel : function(panel)
42057     {
42058         panel = this.getPanel(panel);
42059         if(this.activePanel && this.activePanel != panel){
42060             if(this.activePanel.setActiveState(false) === false){
42061                 return;
42062             }
42063         }
42064         this.activePanel = panel;
42065         panel.setActiveState(true);
42066         if(this.panelSize){
42067             panel.setSize(this.panelSize.width, this.panelSize.height);
42068         }
42069         if(this.closeBtn){
42070             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
42071         }
42072         this.updateTitle(panel.getTitle());
42073         if(this.tabs){
42074             this.fireEvent("invalidated", this);
42075         }
42076         this.fireEvent("panelactivated", this, panel);
42077     },
42078
42079     /**
42080      * Shows the specified panel.
42081      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
42082      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
42083      */
42084     showPanel : function(panel)
42085     {
42086         panel = this.getPanel(panel);
42087         if(panel){
42088             if(this.tabs){
42089                 var tab = this.tabs.getTab(panel.getEl().id);
42090                 if(tab.isHidden()){
42091                     this.tabs.unhideTab(tab.id);
42092                 }
42093                 tab.activate();
42094             }else{
42095                 this.setActivePanel(panel);
42096             }
42097         }
42098         return panel;
42099     },
42100
42101     /**
42102      * Get the active panel for this region.
42103      * @return {Roo.ContentPanel} The active panel or null
42104      */
42105     getActivePanel : function(){
42106         return this.activePanel;
42107     },
42108
42109     validateVisibility : function(){
42110         if(this.panels.getCount() < 1){
42111             this.updateTitle("&#160;");
42112             this.closeBtn.hide();
42113             this.hide();
42114         }else{
42115             if(!this.isVisible()){
42116                 this.show();
42117             }
42118         }
42119     },
42120
42121     /**
42122      * Adds the passed ContentPanel(s) to this region.
42123      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42124      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
42125      */
42126     add : function(panel)
42127     {
42128         if(arguments.length > 1){
42129             for(var i = 0, len = arguments.length; i < len; i++) {
42130                 this.add(arguments[i]);
42131             }
42132             return null;
42133         }
42134         
42135         // if we have not been rendered yet, then we can not really do much of this..
42136         if (!this.bodyEl) {
42137             this.unrendered_panels.push(panel);
42138             return panel;
42139         }
42140         
42141         
42142         
42143         
42144         if(this.hasPanel(panel)){
42145             this.showPanel(panel);
42146             return panel;
42147         }
42148         panel.setRegion(this);
42149         this.panels.add(panel);
42150        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
42151             // sinle panel - no tab...?? would it not be better to render it with the tabs,
42152             // and hide them... ???
42153             this.bodyEl.dom.appendChild(panel.getEl().dom);
42154             if(panel.background !== true){
42155                 this.setActivePanel(panel);
42156             }
42157             this.fireEvent("paneladded", this, panel);
42158             return panel;
42159         }
42160         */
42161         if(!this.tabs){
42162             this.initTabs();
42163         }else{
42164             this.initPanelAsTab(panel);
42165         }
42166         
42167         
42168         if(panel.background !== true){
42169             this.tabs.activate(panel.getEl().id);
42170         }
42171         this.fireEvent("paneladded", this, panel);
42172         return panel;
42173     },
42174
42175     /**
42176      * Hides the tab for the specified panel.
42177      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42178      */
42179     hidePanel : function(panel){
42180         if(this.tabs && (panel = this.getPanel(panel))){
42181             this.tabs.hideTab(panel.getEl().id);
42182         }
42183     },
42184
42185     /**
42186      * Unhides the tab for a previously hidden panel.
42187      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42188      */
42189     unhidePanel : function(panel){
42190         if(this.tabs && (panel = this.getPanel(panel))){
42191             this.tabs.unhideTab(panel.getEl().id);
42192         }
42193     },
42194
42195     clearPanels : function(){
42196         while(this.panels.getCount() > 0){
42197              this.remove(this.panels.first());
42198         }
42199     },
42200
42201     /**
42202      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
42203      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42204      * @param {Boolean} preservePanel Overrides the config preservePanel option
42205      * @return {Roo.ContentPanel} The panel that was removed
42206      */
42207     remove : function(panel, preservePanel)
42208     {
42209         panel = this.getPanel(panel);
42210         if(!panel){
42211             return null;
42212         }
42213         var e = {};
42214         this.fireEvent("beforeremove", this, panel, e);
42215         if(e.cancel === true){
42216             return null;
42217         }
42218         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
42219         var panelId = panel.getId();
42220         this.panels.removeKey(panelId);
42221         if(preservePanel){
42222             document.body.appendChild(panel.getEl().dom);
42223         }
42224         if(this.tabs){
42225             this.tabs.removeTab(panel.getEl().id);
42226         }else if (!preservePanel){
42227             this.bodyEl.dom.removeChild(panel.getEl().dom);
42228         }
42229         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
42230             var p = this.panels.first();
42231             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
42232             tempEl.appendChild(p.getEl().dom);
42233             this.bodyEl.update("");
42234             this.bodyEl.dom.appendChild(p.getEl().dom);
42235             tempEl = null;
42236             this.updateTitle(p.getTitle());
42237             this.tabs = null;
42238             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
42239             this.setActivePanel(p);
42240         }
42241         panel.setRegion(null);
42242         if(this.activePanel == panel){
42243             this.activePanel = null;
42244         }
42245         if(this.config.autoDestroy !== false && preservePanel !== true){
42246             try{panel.destroy();}catch(e){}
42247         }
42248         this.fireEvent("panelremoved", this, panel);
42249         return panel;
42250     },
42251
42252     /**
42253      * Returns the TabPanel component used by this region
42254      * @return {Roo.TabPanel}
42255      */
42256     getTabs : function(){
42257         return this.tabs;
42258     },
42259
42260     createTool : function(parentEl, className){
42261         var btn = Roo.DomHelper.append(parentEl, {
42262             tag: "div",
42263             cls: "x-layout-tools-button",
42264             children: [ {
42265                 tag: "div",
42266                 cls: "roo-layout-tools-button-inner " + className,
42267                 html: "&#160;"
42268             }]
42269         }, true);
42270         btn.addClassOnOver("roo-layout-tools-button-over");
42271         return btn;
42272     }
42273 });/*
42274  * Based on:
42275  * Ext JS Library 1.1.1
42276  * Copyright(c) 2006-2007, Ext JS, LLC.
42277  *
42278  * Originally Released Under LGPL - original licence link has changed is not relivant.
42279  *
42280  * Fork - LGPL
42281  * <script type="text/javascript">
42282  */
42283  
42284
42285
42286 /**
42287  * @class Roo.SplitLayoutRegion
42288  * @extends Roo.LayoutRegion
42289  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
42290  */
42291 Roo.bootstrap.layout.Split = function(config){
42292     this.cursor = config.cursor;
42293     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
42294 };
42295
42296 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
42297 {
42298     splitTip : "Drag to resize.",
42299     collapsibleSplitTip : "Drag to resize. Double click to hide.",
42300     useSplitTips : false,
42301
42302     applyConfig : function(config){
42303         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
42304     },
42305     
42306     onRender : function(ctr,pos) {
42307         
42308         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
42309         if(!this.config.split){
42310             return;
42311         }
42312         if(!this.split){
42313             
42314             var splitEl = Roo.DomHelper.append(ctr.dom,  {
42315                             tag: "div",
42316                             id: this.el.id + "-split",
42317                             cls: "roo-layout-split roo-layout-split-"+this.position,
42318                             html: "&#160;"
42319             });
42320             /** The SplitBar for this region 
42321             * @type Roo.SplitBar */
42322             // does not exist yet...
42323             Roo.log([this.position, this.orientation]);
42324             
42325             this.split = new Roo.bootstrap.SplitBar({
42326                 dragElement : splitEl,
42327                 resizingElement: this.el,
42328                 orientation : this.orientation
42329             });
42330             
42331             this.split.on("moved", this.onSplitMove, this);
42332             this.split.useShim = this.config.useShim === true;
42333             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
42334             if(this.useSplitTips){
42335                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
42336             }
42337             //if(config.collapsible){
42338             //    this.split.el.on("dblclick", this.collapse,  this);
42339             //}
42340         }
42341         if(typeof this.config.minSize != "undefined"){
42342             this.split.minSize = this.config.minSize;
42343         }
42344         if(typeof this.config.maxSize != "undefined"){
42345             this.split.maxSize = this.config.maxSize;
42346         }
42347         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
42348             this.hideSplitter();
42349         }
42350         
42351     },
42352
42353     getHMaxSize : function(){
42354          var cmax = this.config.maxSize || 10000;
42355          var center = this.mgr.getRegion("center");
42356          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
42357     },
42358
42359     getVMaxSize : function(){
42360          var cmax = this.config.maxSize || 10000;
42361          var center = this.mgr.getRegion("center");
42362          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
42363     },
42364
42365     onSplitMove : function(split, newSize){
42366         this.fireEvent("resized", this, newSize);
42367     },
42368     
42369     /** 
42370      * Returns the {@link Roo.SplitBar} for this region.
42371      * @return {Roo.SplitBar}
42372      */
42373     getSplitBar : function(){
42374         return this.split;
42375     },
42376     
42377     hide : function(){
42378         this.hideSplitter();
42379         Roo.bootstrap.layout.Split.superclass.hide.call(this);
42380     },
42381
42382     hideSplitter : function(){
42383         if(this.split){
42384             this.split.el.setLocation(-2000,-2000);
42385             this.split.el.hide();
42386         }
42387     },
42388
42389     show : function(){
42390         if(this.split){
42391             this.split.el.show();
42392         }
42393         Roo.bootstrap.layout.Split.superclass.show.call(this);
42394     },
42395     
42396     beforeSlide: function(){
42397         if(Roo.isGecko){// firefox overflow auto bug workaround
42398             this.bodyEl.clip();
42399             if(this.tabs) {
42400                 this.tabs.bodyEl.clip();
42401             }
42402             if(this.activePanel){
42403                 this.activePanel.getEl().clip();
42404                 
42405                 if(this.activePanel.beforeSlide){
42406                     this.activePanel.beforeSlide();
42407                 }
42408             }
42409         }
42410     },
42411     
42412     afterSlide : function(){
42413         if(Roo.isGecko){// firefox overflow auto bug workaround
42414             this.bodyEl.unclip();
42415             if(this.tabs) {
42416                 this.tabs.bodyEl.unclip();
42417             }
42418             if(this.activePanel){
42419                 this.activePanel.getEl().unclip();
42420                 if(this.activePanel.afterSlide){
42421                     this.activePanel.afterSlide();
42422                 }
42423             }
42424         }
42425     },
42426
42427     initAutoHide : function(){
42428         if(this.autoHide !== false){
42429             if(!this.autoHideHd){
42430                 var st = new Roo.util.DelayedTask(this.slideIn, this);
42431                 this.autoHideHd = {
42432                     "mouseout": function(e){
42433                         if(!e.within(this.el, true)){
42434                             st.delay(500);
42435                         }
42436                     },
42437                     "mouseover" : function(e){
42438                         st.cancel();
42439                     },
42440                     scope : this
42441                 };
42442             }
42443             this.el.on(this.autoHideHd);
42444         }
42445     },
42446
42447     clearAutoHide : function(){
42448         if(this.autoHide !== false){
42449             this.el.un("mouseout", this.autoHideHd.mouseout);
42450             this.el.un("mouseover", this.autoHideHd.mouseover);
42451         }
42452     },
42453
42454     clearMonitor : function(){
42455         Roo.get(document).un("click", this.slideInIf, this);
42456     },
42457
42458     // these names are backwards but not changed for compat
42459     slideOut : function(){
42460         if(this.isSlid || this.el.hasActiveFx()){
42461             return;
42462         }
42463         this.isSlid = true;
42464         if(this.collapseBtn){
42465             this.collapseBtn.hide();
42466         }
42467         this.closeBtnState = this.closeBtn.getStyle('display');
42468         this.closeBtn.hide();
42469         if(this.stickBtn){
42470             this.stickBtn.show();
42471         }
42472         this.el.show();
42473         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
42474         this.beforeSlide();
42475         this.el.setStyle("z-index", 10001);
42476         this.el.slideIn(this.getSlideAnchor(), {
42477             callback: function(){
42478                 this.afterSlide();
42479                 this.initAutoHide();
42480                 Roo.get(document).on("click", this.slideInIf, this);
42481                 this.fireEvent("slideshow", this);
42482             },
42483             scope: this,
42484             block: true
42485         });
42486     },
42487
42488     afterSlideIn : function(){
42489         this.clearAutoHide();
42490         this.isSlid = false;
42491         this.clearMonitor();
42492         this.el.setStyle("z-index", "");
42493         if(this.collapseBtn){
42494             this.collapseBtn.show();
42495         }
42496         this.closeBtn.setStyle('display', this.closeBtnState);
42497         if(this.stickBtn){
42498             this.stickBtn.hide();
42499         }
42500         this.fireEvent("slidehide", this);
42501     },
42502
42503     slideIn : function(cb){
42504         if(!this.isSlid || this.el.hasActiveFx()){
42505             Roo.callback(cb);
42506             return;
42507         }
42508         this.isSlid = false;
42509         this.beforeSlide();
42510         this.el.slideOut(this.getSlideAnchor(), {
42511             callback: function(){
42512                 this.el.setLeftTop(-10000, -10000);
42513                 this.afterSlide();
42514                 this.afterSlideIn();
42515                 Roo.callback(cb);
42516             },
42517             scope: this,
42518             block: true
42519         });
42520     },
42521     
42522     slideInIf : function(e){
42523         if(!e.within(this.el)){
42524             this.slideIn();
42525         }
42526     },
42527
42528     animateCollapse : function(){
42529         this.beforeSlide();
42530         this.el.setStyle("z-index", 20000);
42531         var anchor = this.getSlideAnchor();
42532         this.el.slideOut(anchor, {
42533             callback : function(){
42534                 this.el.setStyle("z-index", "");
42535                 this.collapsedEl.slideIn(anchor, {duration:.3});
42536                 this.afterSlide();
42537                 this.el.setLocation(-10000,-10000);
42538                 this.el.hide();
42539                 this.fireEvent("collapsed", this);
42540             },
42541             scope: this,
42542             block: true
42543         });
42544     },
42545
42546     animateExpand : function(){
42547         this.beforeSlide();
42548         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
42549         this.el.setStyle("z-index", 20000);
42550         this.collapsedEl.hide({
42551             duration:.1
42552         });
42553         this.el.slideIn(this.getSlideAnchor(), {
42554             callback : function(){
42555                 this.el.setStyle("z-index", "");
42556                 this.afterSlide();
42557                 if(this.split){
42558                     this.split.el.show();
42559                 }
42560                 this.fireEvent("invalidated", this);
42561                 this.fireEvent("expanded", this);
42562             },
42563             scope: this,
42564             block: true
42565         });
42566     },
42567
42568     anchors : {
42569         "west" : "left",
42570         "east" : "right",
42571         "north" : "top",
42572         "south" : "bottom"
42573     },
42574
42575     sanchors : {
42576         "west" : "l",
42577         "east" : "r",
42578         "north" : "t",
42579         "south" : "b"
42580     },
42581
42582     canchors : {
42583         "west" : "tl-tr",
42584         "east" : "tr-tl",
42585         "north" : "tl-bl",
42586         "south" : "bl-tl"
42587     },
42588
42589     getAnchor : function(){
42590         return this.anchors[this.position];
42591     },
42592
42593     getCollapseAnchor : function(){
42594         return this.canchors[this.position];
42595     },
42596
42597     getSlideAnchor : function(){
42598         return this.sanchors[this.position];
42599     },
42600
42601     getAlignAdj : function(){
42602         var cm = this.cmargins;
42603         switch(this.position){
42604             case "west":
42605                 return [0, 0];
42606             break;
42607             case "east":
42608                 return [0, 0];
42609             break;
42610             case "north":
42611                 return [0, 0];
42612             break;
42613             case "south":
42614                 return [0, 0];
42615             break;
42616         }
42617     },
42618
42619     getExpandAdj : function(){
42620         var c = this.collapsedEl, cm = this.cmargins;
42621         switch(this.position){
42622             case "west":
42623                 return [-(cm.right+c.getWidth()+cm.left), 0];
42624             break;
42625             case "east":
42626                 return [cm.right+c.getWidth()+cm.left, 0];
42627             break;
42628             case "north":
42629                 return [0, -(cm.top+cm.bottom+c.getHeight())];
42630             break;
42631             case "south":
42632                 return [0, cm.top+cm.bottom+c.getHeight()];
42633             break;
42634         }
42635     }
42636 });/*
42637  * Based on:
42638  * Ext JS Library 1.1.1
42639  * Copyright(c) 2006-2007, Ext JS, LLC.
42640  *
42641  * Originally Released Under LGPL - original licence link has changed is not relivant.
42642  *
42643  * Fork - LGPL
42644  * <script type="text/javascript">
42645  */
42646 /*
42647  * These classes are private internal classes
42648  */
42649 Roo.bootstrap.layout.Center = function(config){
42650     config.region = "center";
42651     Roo.bootstrap.layout.Region.call(this, config);
42652     this.visible = true;
42653     this.minWidth = config.minWidth || 20;
42654     this.minHeight = config.minHeight || 20;
42655 };
42656
42657 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
42658     hide : function(){
42659         // center panel can't be hidden
42660     },
42661     
42662     show : function(){
42663         // center panel can't be hidden
42664     },
42665     
42666     getMinWidth: function(){
42667         return this.minWidth;
42668     },
42669     
42670     getMinHeight: function(){
42671         return this.minHeight;
42672     }
42673 });
42674
42675
42676
42677
42678  
42679
42680
42681
42682
42683
42684
42685 Roo.bootstrap.layout.North = function(config)
42686 {
42687     config.region = 'north';
42688     config.cursor = 'n-resize';
42689     
42690     Roo.bootstrap.layout.Split.call(this, config);
42691     
42692     
42693     if(this.split){
42694         this.split.placement = Roo.bootstrap.SplitBar.TOP;
42695         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42696         this.split.el.addClass("roo-layout-split-v");
42697     }
42698     //var size = config.initialSize || config.height;
42699     //if(this.el && typeof size != "undefined"){
42700     //    this.el.setHeight(size);
42701     //}
42702 };
42703 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
42704 {
42705     orientation: Roo.bootstrap.SplitBar.VERTICAL,
42706      
42707      
42708     onRender : function(ctr, pos)
42709     {
42710         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42711         var size = this.config.initialSize || this.config.height;
42712         if(this.el && typeof size != "undefined"){
42713             this.el.setHeight(size);
42714         }
42715     
42716     },
42717     
42718     getBox : function(){
42719         if(this.collapsed){
42720             return this.collapsedEl.getBox();
42721         }
42722         var box = this.el.getBox();
42723         if(this.split){
42724             box.height += this.split.el.getHeight();
42725         }
42726         return box;
42727     },
42728     
42729     updateBox : function(box){
42730         if(this.split && !this.collapsed){
42731             box.height -= this.split.el.getHeight();
42732             this.split.el.setLeft(box.x);
42733             this.split.el.setTop(box.y+box.height);
42734             this.split.el.setWidth(box.width);
42735         }
42736         if(this.collapsed){
42737             this.updateBody(box.width, null);
42738         }
42739         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42740     }
42741 });
42742
42743
42744
42745
42746
42747 Roo.bootstrap.layout.South = function(config){
42748     config.region = 'south';
42749     config.cursor = 's-resize';
42750     Roo.bootstrap.layout.Split.call(this, config);
42751     if(this.split){
42752         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
42753         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42754         this.split.el.addClass("roo-layout-split-v");
42755     }
42756     
42757 };
42758
42759 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
42760     orientation: Roo.bootstrap.SplitBar.VERTICAL,
42761     
42762     onRender : function(ctr, pos)
42763     {
42764         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42765         var size = this.config.initialSize || this.config.height;
42766         if(this.el && typeof size != "undefined"){
42767             this.el.setHeight(size);
42768         }
42769     
42770     },
42771     
42772     getBox : function(){
42773         if(this.collapsed){
42774             return this.collapsedEl.getBox();
42775         }
42776         var box = this.el.getBox();
42777         if(this.split){
42778             var sh = this.split.el.getHeight();
42779             box.height += sh;
42780             box.y -= sh;
42781         }
42782         return box;
42783     },
42784     
42785     updateBox : function(box){
42786         if(this.split && !this.collapsed){
42787             var sh = this.split.el.getHeight();
42788             box.height -= sh;
42789             box.y += sh;
42790             this.split.el.setLeft(box.x);
42791             this.split.el.setTop(box.y-sh);
42792             this.split.el.setWidth(box.width);
42793         }
42794         if(this.collapsed){
42795             this.updateBody(box.width, null);
42796         }
42797         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42798     }
42799 });
42800
42801 Roo.bootstrap.layout.East = function(config){
42802     config.region = "east";
42803     config.cursor = "e-resize";
42804     Roo.bootstrap.layout.Split.call(this, config);
42805     if(this.split){
42806         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
42807         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
42808         this.split.el.addClass("roo-layout-split-h");
42809     }
42810     
42811 };
42812 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
42813     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
42814     
42815     onRender : function(ctr, pos)
42816     {
42817         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42818         var size = this.config.initialSize || this.config.width;
42819         if(this.el && typeof size != "undefined"){
42820             this.el.setWidth(size);
42821         }
42822     
42823     },
42824     
42825     getBox : function(){
42826         if(this.collapsed){
42827             return this.collapsedEl.getBox();
42828         }
42829         var box = this.el.getBox();
42830         if(this.split){
42831             var sw = this.split.el.getWidth();
42832             box.width += sw;
42833             box.x -= sw;
42834         }
42835         return box;
42836     },
42837
42838     updateBox : function(box){
42839         if(this.split && !this.collapsed){
42840             var sw = this.split.el.getWidth();
42841             box.width -= sw;
42842             this.split.el.setLeft(box.x);
42843             this.split.el.setTop(box.y);
42844             this.split.el.setHeight(box.height);
42845             box.x += sw;
42846         }
42847         if(this.collapsed){
42848             this.updateBody(null, box.height);
42849         }
42850         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42851     }
42852 });
42853
42854 Roo.bootstrap.layout.West = function(config){
42855     config.region = "west";
42856     config.cursor = "w-resize";
42857     
42858     Roo.bootstrap.layout.Split.call(this, config);
42859     if(this.split){
42860         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
42861         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
42862         this.split.el.addClass("roo-layout-split-h");
42863     }
42864     
42865 };
42866 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
42867     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
42868     
42869     onRender: function(ctr, pos)
42870     {
42871         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
42872         var size = this.config.initialSize || this.config.width;
42873         if(typeof size != "undefined"){
42874             this.el.setWidth(size);
42875         }
42876     },
42877     
42878     getBox : function(){
42879         if(this.collapsed){
42880             return this.collapsedEl.getBox();
42881         }
42882         var box = this.el.getBox();
42883         if (box.width == 0) {
42884             box.width = this.config.width; // kludge?
42885         }
42886         if(this.split){
42887             box.width += this.split.el.getWidth();
42888         }
42889         return box;
42890     },
42891     
42892     updateBox : function(box){
42893         if(this.split && !this.collapsed){
42894             var sw = this.split.el.getWidth();
42895             box.width -= sw;
42896             this.split.el.setLeft(box.x+box.width);
42897             this.split.el.setTop(box.y);
42898             this.split.el.setHeight(box.height);
42899         }
42900         if(this.collapsed){
42901             this.updateBody(null, box.height);
42902         }
42903         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42904     }
42905 });/*
42906  * Based on:
42907  * Ext JS Library 1.1.1
42908  * Copyright(c) 2006-2007, Ext JS, LLC.
42909  *
42910  * Originally Released Under LGPL - original licence link has changed is not relivant.
42911  *
42912  * Fork - LGPL
42913  * <script type="text/javascript">
42914  */
42915 /**
42916  * @class Roo.bootstrap.paenl.Content
42917  * @extends Roo.util.Observable
42918  * @children Roo.bootstrap.Component
42919  * @parent builder Roo.bootstrap.layout.Border
42920  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
42921  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
42922  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
42923  * @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
42924  * @cfg {Boolean}   closable      True if the panel can be closed/removed
42925  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
42926  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
42927  * @cfg {Toolbar}   toolbar       A toolbar for this panel
42928  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
42929  * @cfg {String} title          The title for this panel
42930  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
42931  * @cfg {String} url            Calls {@link #setUrl} with this value
42932  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
42933  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
42934  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
42935  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
42936  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
42937  * @cfg {Boolean} badges render the badges
42938  * @cfg {String} cls  extra classes to use  
42939  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
42940  
42941  * @constructor
42942  * Create a new ContentPanel.
42943  * @param {String/Object} config A string to set only the title or a config object
42944  
42945  */
42946 Roo.bootstrap.panel.Content = function( config){
42947     
42948     this.tpl = config.tpl || false;
42949     
42950     var el = config.el;
42951     var content = config.content;
42952
42953     if(config.autoCreate){ // xtype is available if this is called from factory
42954         el = Roo.id();
42955     }
42956     this.el = Roo.get(el);
42957     if(!this.el && config && config.autoCreate){
42958         if(typeof config.autoCreate == "object"){
42959             if(!config.autoCreate.id){
42960                 config.autoCreate.id = config.id||el;
42961             }
42962             this.el = Roo.DomHelper.append(document.body,
42963                         config.autoCreate, true);
42964         }else{
42965             var elcfg =  {
42966                 tag: "div",
42967                 cls: (config.cls || '') +
42968                     (config.background ? ' bg-' + config.background : '') +
42969                     " roo-layout-inactive-content",
42970                 id: config.id||el
42971             };
42972             if (config.iframe) {
42973                 elcfg.cn = [
42974                     {
42975                         tag : 'iframe',
42976                         style : 'border: 0px',
42977                         src : 'about:blank'
42978                     }
42979                 ];
42980             }
42981               
42982             if (config.html) {
42983                 elcfg.html = config.html;
42984                 
42985             }
42986                         
42987             this.el = Roo.DomHelper.append(document.body, elcfg , true);
42988             if (config.iframe) {
42989                 this.iframeEl = this.el.select('iframe',true).first();
42990             }
42991             
42992         }
42993     } 
42994     this.closable = false;
42995     this.loaded = false;
42996     this.active = false;
42997    
42998       
42999     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
43000         
43001         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
43002         
43003         this.wrapEl = this.el; //this.el.wrap();
43004         var ti = [];
43005         if (config.toolbar.items) {
43006             ti = config.toolbar.items ;
43007             delete config.toolbar.items ;
43008         }
43009         
43010         var nitems = [];
43011         this.toolbar.render(this.wrapEl, 'before');
43012         for(var i =0;i < ti.length;i++) {
43013           //  Roo.log(['add child', items[i]]);
43014             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43015         }
43016         this.toolbar.items = nitems;
43017         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
43018         delete config.toolbar;
43019         
43020     }
43021     /*
43022     // xtype created footer. - not sure if will work as we normally have to render first..
43023     if (this.footer && !this.footer.el && this.footer.xtype) {
43024         if (!this.wrapEl) {
43025             this.wrapEl = this.el.wrap();
43026         }
43027     
43028         this.footer.container = this.wrapEl.createChild();
43029          
43030         this.footer = Roo.factory(this.footer, Roo);
43031         
43032     }
43033     */
43034     
43035      if(typeof config == "string"){
43036         this.title = config;
43037     }else{
43038         Roo.apply(this, config);
43039     }
43040     
43041     if(this.resizeEl){
43042         this.resizeEl = Roo.get(this.resizeEl, true);
43043     }else{
43044         this.resizeEl = this.el;
43045     }
43046     // handle view.xtype
43047     
43048  
43049     
43050     
43051     this.addEvents({
43052         /**
43053          * @event activate
43054          * Fires when this panel is activated. 
43055          * @param {Roo.ContentPanel} this
43056          */
43057         "activate" : true,
43058         /**
43059          * @event deactivate
43060          * Fires when this panel is activated. 
43061          * @param {Roo.ContentPanel} this
43062          */
43063         "deactivate" : true,
43064
43065         /**
43066          * @event resize
43067          * Fires when this panel is resized if fitToFrame is true.
43068          * @param {Roo.ContentPanel} this
43069          * @param {Number} width The width after any component adjustments
43070          * @param {Number} height The height after any component adjustments
43071          */
43072         "resize" : true,
43073         
43074          /**
43075          * @event render
43076          * Fires when this tab is created
43077          * @param {Roo.ContentPanel} this
43078          */
43079         "render" : true,
43080         
43081           /**
43082          * @event scroll
43083          * Fires when this content is scrolled
43084          * @param {Roo.ContentPanel} this
43085          * @param {Event} scrollEvent
43086          */
43087         "scroll" : true
43088         
43089         
43090         
43091     });
43092     
43093
43094     
43095     
43096     if(this.autoScroll && !this.iframe){
43097         this.resizeEl.setStyle("overflow", "auto");
43098         this.resizeEl.on('scroll', this.onScroll, this);
43099     } else {
43100         // fix randome scrolling
43101         //this.el.on('scroll', function() {
43102         //    Roo.log('fix random scolling');
43103         //    this.scrollTo('top',0); 
43104         //});
43105     }
43106     content = content || this.content;
43107     if(content){
43108         this.setContent(content);
43109     }
43110     if(config && config.url){
43111         this.setUrl(this.url, this.params, this.loadOnce);
43112     }
43113     
43114     
43115     
43116     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
43117     
43118     if (this.view && typeof(this.view.xtype) != 'undefined') {
43119         this.view.el = this.el.appendChild(document.createElement("div"));
43120         this.view = Roo.factory(this.view); 
43121         this.view.render  &&  this.view.render(false, '');  
43122     }
43123     
43124     
43125     this.fireEvent('render', this);
43126 };
43127
43128 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
43129     
43130     cls : '',
43131     background : '',
43132     
43133     tabTip : '',
43134     
43135     iframe : false,
43136     iframeEl : false,
43137     
43138     /* Resize Element - use this to work out scroll etc. */
43139     resizeEl : false,
43140     
43141     setRegion : function(region){
43142         this.region = region;
43143         this.setActiveClass(region && !this.background);
43144     },
43145     
43146     
43147     setActiveClass: function(state)
43148     {
43149         if(state){
43150            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
43151            this.el.setStyle('position','relative');
43152         }else{
43153            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
43154            this.el.setStyle('position', 'absolute');
43155         } 
43156     },
43157     
43158     /**
43159      * Returns the toolbar for this Panel if one was configured. 
43160      * @return {Roo.Toolbar} 
43161      */
43162     getToolbar : function(){
43163         return this.toolbar;
43164     },
43165     
43166     setActiveState : function(active)
43167     {
43168         this.active = active;
43169         this.setActiveClass(active);
43170         if(!active){
43171             if(this.fireEvent("deactivate", this) === false){
43172                 return false;
43173             }
43174             return true;
43175         }
43176         this.fireEvent("activate", this);
43177         return true;
43178     },
43179     /**
43180      * Updates this panel's element (not for iframe)
43181      * @param {String} content The new content
43182      * @param {Boolean} loadScripts (optional) true to look for and process scripts
43183     */
43184     setContent : function(content, loadScripts){
43185         if (this.iframe) {
43186             return;
43187         }
43188         
43189         this.el.update(content, loadScripts);
43190     },
43191
43192     ignoreResize : function(w, h)
43193     {
43194         //return false; // always resize?
43195         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
43196             return true;
43197         }else{
43198             this.lastSize = {width: w, height: h};
43199             return false;
43200         }
43201     },
43202     /**
43203      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
43204      * @return {Roo.UpdateManager} The UpdateManager
43205      */
43206     getUpdateManager : function(){
43207         if (this.iframe) {
43208             return false;
43209         }
43210         return this.el.getUpdateManager();
43211     },
43212      /**
43213      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
43214      * Does not work with IFRAME contents
43215      * @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:
43216 <pre><code>
43217 panel.load({
43218     url: "your-url.php",
43219     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
43220     callback: yourFunction,
43221     scope: yourObject, //(optional scope)
43222     discardUrl: false,
43223     nocache: false,
43224     text: "Loading...",
43225     timeout: 30,
43226     scripts: false
43227 });
43228 </code></pre>
43229      
43230      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
43231      * 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.
43232      * @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}
43233      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
43234      * @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.
43235      * @return {Roo.ContentPanel} this
43236      */
43237     load : function(){
43238         
43239         if (this.iframe) {
43240             return this;
43241         }
43242         
43243         var um = this.el.getUpdateManager();
43244         um.update.apply(um, arguments);
43245         return this;
43246     },
43247
43248
43249     /**
43250      * 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.
43251      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
43252      * @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)
43253      * @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)
43254      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
43255      */
43256     setUrl : function(url, params, loadOnce){
43257         if (this.iframe) {
43258             this.iframeEl.dom.src = url;
43259             return false;
43260         }
43261         
43262         if(this.refreshDelegate){
43263             this.removeListener("activate", this.refreshDelegate);
43264         }
43265         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
43266         this.on("activate", this.refreshDelegate);
43267         return this.el.getUpdateManager();
43268     },
43269     
43270     _handleRefresh : function(url, params, loadOnce){
43271         if(!loadOnce || !this.loaded){
43272             var updater = this.el.getUpdateManager();
43273             updater.update(url, params, this._setLoaded.createDelegate(this));
43274         }
43275     },
43276     
43277     _setLoaded : function(){
43278         this.loaded = true;
43279     }, 
43280     
43281     /**
43282      * Returns this panel's id
43283      * @return {String} 
43284      */
43285     getId : function(){
43286         return this.el.id;
43287     },
43288     
43289     /** 
43290      * Returns this panel's element - used by regiosn to add.
43291      * @return {Roo.Element} 
43292      */
43293     getEl : function(){
43294         return this.wrapEl || this.el;
43295     },
43296     
43297    
43298     
43299     adjustForComponents : function(width, height)
43300     {
43301         //Roo.log('adjustForComponents ');
43302         if(this.resizeEl != this.el){
43303             width -= this.el.getFrameWidth('lr');
43304             height -= this.el.getFrameWidth('tb');
43305         }
43306         if(this.toolbar){
43307             var te = this.toolbar.getEl();
43308             te.setWidth(width);
43309             height -= te.getHeight();
43310         }
43311         if(this.footer){
43312             var te = this.footer.getEl();
43313             te.setWidth(width);
43314             height -= te.getHeight();
43315         }
43316         
43317         
43318         if(this.adjustments){
43319             width += this.adjustments[0];
43320             height += this.adjustments[1];
43321         }
43322         return {"width": width, "height": height};
43323     },
43324     
43325     setSize : function(width, height){
43326         if(this.fitToFrame && !this.ignoreResize(width, height)){
43327             if(this.fitContainer && this.resizeEl != this.el){
43328                 this.el.setSize(width, height);
43329             }
43330             var size = this.adjustForComponents(width, height);
43331             if (this.iframe) {
43332                 this.iframeEl.setSize(width,height);
43333             }
43334             
43335             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
43336             this.fireEvent('resize', this, size.width, size.height);
43337             
43338             
43339         }
43340     },
43341     
43342     /**
43343      * Returns this panel's title
43344      * @return {String} 
43345      */
43346     getTitle : function(){
43347         
43348         if (typeof(this.title) != 'object') {
43349             return this.title;
43350         }
43351         
43352         var t = '';
43353         for (var k in this.title) {
43354             if (!this.title.hasOwnProperty(k)) {
43355                 continue;
43356             }
43357             
43358             if (k.indexOf('-') >= 0) {
43359                 var s = k.split('-');
43360                 for (var i = 0; i<s.length; i++) {
43361                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
43362                 }
43363             } else {
43364                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
43365             }
43366         }
43367         return t;
43368     },
43369     
43370     /**
43371      * Set this panel's title
43372      * @param {String} title
43373      */
43374     setTitle : function(title){
43375         this.title = title;
43376         if(this.region){
43377             this.region.updatePanelTitle(this, title);
43378         }
43379     },
43380     
43381     /**
43382      * Returns true is this panel was configured to be closable
43383      * @return {Boolean} 
43384      */
43385     isClosable : function(){
43386         return this.closable;
43387     },
43388     
43389     beforeSlide : function(){
43390         this.el.clip();
43391         this.resizeEl.clip();
43392     },
43393     
43394     afterSlide : function(){
43395         this.el.unclip();
43396         this.resizeEl.unclip();
43397     },
43398     
43399     /**
43400      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
43401      *   Will fail silently if the {@link #setUrl} method has not been called.
43402      *   This does not activate the panel, just updates its content.
43403      */
43404     refresh : function(){
43405         if(this.refreshDelegate){
43406            this.loaded = false;
43407            this.refreshDelegate();
43408         }
43409     },
43410     
43411     /**
43412      * Destroys this panel
43413      */
43414     destroy : function(){
43415         this.el.removeAllListeners();
43416         var tempEl = document.createElement("span");
43417         tempEl.appendChild(this.el.dom);
43418         tempEl.innerHTML = "";
43419         this.el.remove();
43420         this.el = null;
43421     },
43422     
43423     /**
43424      * form - if the content panel contains a form - this is a reference to it.
43425      * @type {Roo.form.Form}
43426      */
43427     form : false,
43428     /**
43429      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
43430      *    This contains a reference to it.
43431      * @type {Roo.View}
43432      */
43433     view : false,
43434     
43435       /**
43436      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
43437      * <pre><code>
43438
43439 layout.addxtype({
43440        xtype : 'Form',
43441        items: [ .... ]
43442    }
43443 );
43444
43445 </code></pre>
43446      * @param {Object} cfg Xtype definition of item to add.
43447      */
43448     
43449     
43450     getChildContainer: function () {
43451         return this.getEl();
43452     },
43453     
43454     
43455     onScroll : function(e)
43456     {
43457         this.fireEvent('scroll', this, e);
43458     }
43459     
43460     
43461     /*
43462         var  ret = new Roo.factory(cfg);
43463         return ret;
43464         
43465         
43466         // add form..
43467         if (cfg.xtype.match(/^Form$/)) {
43468             
43469             var el;
43470             //if (this.footer) {
43471             //    el = this.footer.container.insertSibling(false, 'before');
43472             //} else {
43473                 el = this.el.createChild();
43474             //}
43475
43476             this.form = new  Roo.form.Form(cfg);
43477             
43478             
43479             if ( this.form.allItems.length) {
43480                 this.form.render(el.dom);
43481             }
43482             return this.form;
43483         }
43484         // should only have one of theses..
43485         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
43486             // views.. should not be just added - used named prop 'view''
43487             
43488             cfg.el = this.el.appendChild(document.createElement("div"));
43489             // factory?
43490             
43491             var ret = new Roo.factory(cfg);
43492              
43493              ret.render && ret.render(false, ''); // render blank..
43494             this.view = ret;
43495             return ret;
43496         }
43497         return false;
43498     }
43499     \*/
43500 });
43501  
43502 /**
43503  * @class Roo.bootstrap.panel.Grid
43504  * @extends Roo.bootstrap.panel.Content
43505  * @constructor
43506  * Create a new GridPanel.
43507  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
43508  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
43509  * @param {Object} config A the config object
43510   
43511  */
43512
43513
43514
43515 Roo.bootstrap.panel.Grid = function(config)
43516 {
43517     
43518       
43519     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
43520         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
43521
43522     config.el = this.wrapper;
43523     //this.el = this.wrapper;
43524     
43525       if (config.container) {
43526         // ctor'ed from a Border/panel.grid
43527         
43528         
43529         this.wrapper.setStyle("overflow", "hidden");
43530         this.wrapper.addClass('roo-grid-container');
43531
43532     }
43533     
43534     
43535     if(config.toolbar){
43536         var tool_el = this.wrapper.createChild();    
43537         this.toolbar = Roo.factory(config.toolbar);
43538         var ti = [];
43539         if (config.toolbar.items) {
43540             ti = config.toolbar.items ;
43541             delete config.toolbar.items ;
43542         }
43543         
43544         var nitems = [];
43545         this.toolbar.render(tool_el);
43546         for(var i =0;i < ti.length;i++) {
43547           //  Roo.log(['add child', items[i]]);
43548             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43549         }
43550         this.toolbar.items = nitems;
43551         
43552         delete config.toolbar;
43553     }
43554     
43555     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
43556     config.grid.scrollBody = true;;
43557     config.grid.monitorWindowResize = false; // turn off autosizing
43558     config.grid.autoHeight = false;
43559     config.grid.autoWidth = false;
43560     
43561     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
43562     
43563     if (config.background) {
43564         // render grid on panel activation (if panel background)
43565         this.on('activate', function(gp) {
43566             if (!gp.grid.rendered) {
43567                 gp.grid.render(this.wrapper);
43568                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
43569             }
43570         });
43571             
43572     } else {
43573         this.grid.render(this.wrapper);
43574         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
43575
43576     }
43577     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
43578     // ??? needed ??? config.el = this.wrapper;
43579     
43580     
43581     
43582   
43583     // xtype created footer. - not sure if will work as we normally have to render first..
43584     if (this.footer && !this.footer.el && this.footer.xtype) {
43585         
43586         var ctr = this.grid.getView().getFooterPanel(true);
43587         this.footer.dataSource = this.grid.dataSource;
43588         this.footer = Roo.factory(this.footer, Roo);
43589         this.footer.render(ctr);
43590         
43591     }
43592     
43593     
43594     
43595     
43596      
43597 };
43598
43599 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
43600 {
43601   
43602     getId : function(){
43603         return this.grid.id;
43604     },
43605     
43606     /**
43607      * Returns the grid for this panel
43608      * @return {Roo.bootstrap.Table} 
43609      */
43610     getGrid : function(){
43611         return this.grid;    
43612     },
43613     
43614     setSize : function(width, height)
43615     {
43616      
43617         //if(!this.ignoreResize(width, height)){
43618             var grid = this.grid;
43619             var size = this.adjustForComponents(width, height);
43620             // tfoot is not a footer?
43621           
43622             
43623             var gridel = grid.getGridEl();
43624             gridel.setSize(size.width, size.height);
43625             
43626             var tbd = grid.getGridEl().select('tbody', true).first();
43627             var thd = grid.getGridEl().select('thead',true).first();
43628             var tbf= grid.getGridEl().select('tfoot', true).first();
43629
43630             if (tbf) {
43631                 size.height -= tbf.getHeight();
43632             }
43633             if (thd) {
43634                 size.height -= thd.getHeight();
43635             }
43636             
43637             tbd.setSize(size.width, size.height );
43638             // this is for the account management tab -seems to work there.
43639             var thd = grid.getGridEl().select('thead',true).first();
43640             //if (tbd) {
43641             //    tbd.setSize(size.width, size.height - thd.getHeight());
43642             //}
43643              
43644             grid.autoSize();
43645         //}
43646    
43647     },
43648      
43649     
43650     
43651     beforeSlide : function(){
43652         this.grid.getView().scroller.clip();
43653     },
43654     
43655     afterSlide : function(){
43656         this.grid.getView().scroller.unclip();
43657     },
43658     
43659     destroy : function(){
43660         this.grid.destroy();
43661         delete this.grid;
43662         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
43663     }
43664 });
43665
43666 /**
43667  * @class Roo.bootstrap.panel.Nest
43668  * @extends Roo.bootstrap.panel.Content
43669  * @constructor
43670  * Create a new Panel, that can contain a layout.Border.
43671  * 
43672  * 
43673  * @param {String/Object} config A string to set only the title or a config object
43674  */
43675 Roo.bootstrap.panel.Nest = function(config)
43676 {
43677     // construct with only one argument..
43678     /* FIXME - implement nicer consturctors
43679     if (layout.layout) {
43680         config = layout;
43681         layout = config.layout;
43682         delete config.layout;
43683     }
43684     if (layout.xtype && !layout.getEl) {
43685         // then layout needs constructing..
43686         layout = Roo.factory(layout, Roo);
43687     }
43688     */
43689     
43690     config.el =  config.layout.getEl();
43691     
43692     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
43693     
43694     config.layout.monitorWindowResize = false; // turn off autosizing
43695     this.layout = config.layout;
43696     this.layout.getEl().addClass("roo-layout-nested-layout");
43697     this.layout.parent = this;
43698     
43699     
43700     
43701     
43702 };
43703
43704 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
43705     /**
43706     * @cfg {Roo.BorderLayout} layout The layout for this panel
43707     */
43708     layout : false,
43709
43710     setSize : function(width, height){
43711         if(!this.ignoreResize(width, height)){
43712             var size = this.adjustForComponents(width, height);
43713             var el = this.layout.getEl();
43714             if (size.height < 1) {
43715                 el.setWidth(size.width);   
43716             } else {
43717                 el.setSize(size.width, size.height);
43718             }
43719             var touch = el.dom.offsetWidth;
43720             this.layout.layout();
43721             // ie requires a double layout on the first pass
43722             if(Roo.isIE && !this.initialized){
43723                 this.initialized = true;
43724                 this.layout.layout();
43725             }
43726         }
43727     },
43728     
43729     // activate all subpanels if not currently active..
43730     
43731     setActiveState : function(active){
43732         this.active = active;
43733         this.setActiveClass(active);
43734         
43735         if(!active){
43736             this.fireEvent("deactivate", this);
43737             return;
43738         }
43739         
43740         this.fireEvent("activate", this);
43741         // not sure if this should happen before or after..
43742         if (!this.layout) {
43743             return; // should not happen..
43744         }
43745         var reg = false;
43746         for (var r in this.layout.regions) {
43747             reg = this.layout.getRegion(r);
43748             if (reg.getActivePanel()) {
43749                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
43750                 reg.setActivePanel(reg.getActivePanel());
43751                 continue;
43752             }
43753             if (!reg.panels.length) {
43754                 continue;
43755             }
43756             reg.showPanel(reg.getPanel(0));
43757         }
43758         
43759         
43760         
43761         
43762     },
43763     
43764     /**
43765      * Returns the nested BorderLayout for this panel
43766      * @return {Roo.BorderLayout} 
43767      */
43768     getLayout : function(){
43769         return this.layout;
43770     },
43771     
43772      /**
43773      * Adds a xtype elements to the layout of the nested panel
43774      * <pre><code>
43775
43776 panel.addxtype({
43777        xtype : 'ContentPanel',
43778        region: 'west',
43779        items: [ .... ]
43780    }
43781 );
43782
43783 panel.addxtype({
43784         xtype : 'NestedLayoutPanel',
43785         region: 'west',
43786         layout: {
43787            center: { },
43788            west: { }   
43789         },
43790         items : [ ... list of content panels or nested layout panels.. ]
43791    }
43792 );
43793 </code></pre>
43794      * @param {Object} cfg Xtype definition of item to add.
43795      */
43796     addxtype : function(cfg) {
43797         return this.layout.addxtype(cfg);
43798     
43799     }
43800 });/*
43801  * Based on:
43802  * Ext JS Library 1.1.1
43803  * Copyright(c) 2006-2007, Ext JS, LLC.
43804  *
43805  * Originally Released Under LGPL - original licence link has changed is not relivant.
43806  *
43807  * Fork - LGPL
43808  * <script type="text/javascript">
43809  */
43810 /**
43811  * @class Roo.TabPanel
43812  * @extends Roo.util.Observable
43813  * A lightweight tab container.
43814  * <br><br>
43815  * Usage:
43816  * <pre><code>
43817 // basic tabs 1, built from existing content
43818 var tabs = new Roo.TabPanel("tabs1");
43819 tabs.addTab("script", "View Script");
43820 tabs.addTab("markup", "View Markup");
43821 tabs.activate("script");
43822
43823 // more advanced tabs, built from javascript
43824 var jtabs = new Roo.TabPanel("jtabs");
43825 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
43826
43827 // set up the UpdateManager
43828 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
43829 var updater = tab2.getUpdateManager();
43830 updater.setDefaultUrl("ajax1.htm");
43831 tab2.on('activate', updater.refresh, updater, true);
43832
43833 // Use setUrl for Ajax loading
43834 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
43835 tab3.setUrl("ajax2.htm", null, true);
43836
43837 // Disabled tab
43838 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
43839 tab4.disable();
43840
43841 jtabs.activate("jtabs-1");
43842  * </code></pre>
43843  * @constructor
43844  * Create a new TabPanel.
43845  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
43846  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
43847  */
43848 Roo.bootstrap.panel.Tabs = function(config){
43849     /**
43850     * The container element for this TabPanel.
43851     * @type Roo.Element
43852     */
43853     this.el = Roo.get(config.el);
43854     delete config.el;
43855     if(config){
43856         if(typeof config == "boolean"){
43857             this.tabPosition = config ? "bottom" : "top";
43858         }else{
43859             Roo.apply(this, config);
43860         }
43861     }
43862     
43863     if(this.tabPosition == "bottom"){
43864         // if tabs are at the bottom = create the body first.
43865         this.bodyEl = Roo.get(this.createBody(this.el.dom));
43866         this.el.addClass("roo-tabs-bottom");
43867     }
43868     // next create the tabs holders
43869     
43870     if (this.tabPosition == "west"){
43871         
43872         var reg = this.region; // fake it..
43873         while (reg) {
43874             if (!reg.mgr.parent) {
43875                 break;
43876             }
43877             reg = reg.mgr.parent.region;
43878         }
43879         Roo.log("got nest?");
43880         Roo.log(reg);
43881         if (reg.mgr.getRegion('west')) {
43882             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
43883             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
43884             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
43885             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
43886             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
43887         
43888             
43889         }
43890         
43891         
43892     } else {
43893      
43894         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
43895         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
43896         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
43897         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
43898     }
43899     
43900     
43901     if(Roo.isIE){
43902         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
43903     }
43904     
43905     // finally - if tabs are at the top, then create the body last..
43906     if(this.tabPosition != "bottom"){
43907         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
43908          * @type Roo.Element
43909          */
43910         this.bodyEl = Roo.get(this.createBody(this.el.dom));
43911         this.el.addClass("roo-tabs-top");
43912     }
43913     this.items = [];
43914
43915     this.bodyEl.setStyle("position", "relative");
43916
43917     this.active = null;
43918     this.activateDelegate = this.activate.createDelegate(this);
43919
43920     this.addEvents({
43921         /**
43922          * @event tabchange
43923          * Fires when the active tab changes
43924          * @param {Roo.TabPanel} this
43925          * @param {Roo.TabPanelItem} activePanel The new active tab
43926          */
43927         "tabchange": true,
43928         /**
43929          * @event beforetabchange
43930          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
43931          * @param {Roo.TabPanel} this
43932          * @param {Object} e Set cancel to true on this object to cancel the tab change
43933          * @param {Roo.TabPanelItem} tab The tab being changed to
43934          */
43935         "beforetabchange" : true
43936     });
43937
43938     Roo.EventManager.onWindowResize(this.onResize, this);
43939     this.cpad = this.el.getPadding("lr");
43940     this.hiddenCount = 0;
43941
43942
43943     // toolbar on the tabbar support...
43944     if (this.toolbar) {
43945         alert("no toolbar support yet");
43946         this.toolbar  = false;
43947         /*
43948         var tcfg = this.toolbar;
43949         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
43950         this.toolbar = new Roo.Toolbar(tcfg);
43951         if (Roo.isSafari) {
43952             var tbl = tcfg.container.child('table', true);
43953             tbl.setAttribute('width', '100%');
43954         }
43955         */
43956         
43957     }
43958    
43959
43960
43961     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
43962 };
43963
43964 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
43965     /*
43966      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
43967      */
43968     tabPosition : "top",
43969     /*
43970      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
43971      */
43972     currentTabWidth : 0,
43973     /*
43974      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
43975      */
43976     minTabWidth : 40,
43977     /*
43978      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
43979      */
43980     maxTabWidth : 250,
43981     /*
43982      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
43983      */
43984     preferredTabWidth : 175,
43985     /*
43986      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
43987      */
43988     resizeTabs : false,
43989     /*
43990      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
43991      */
43992     monitorResize : true,
43993     /*
43994      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
43995      */
43996     toolbar : false,  // set by caller..
43997     
43998     region : false, /// set by caller
43999     
44000     disableTooltips : true, // not used yet...
44001
44002     /**
44003      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
44004      * @param {String} id The id of the div to use <b>or create</b>
44005      * @param {String} text The text for the tab
44006      * @param {String} content (optional) Content to put in the TabPanelItem body
44007      * @param {Boolean} closable (optional) True to create a close icon on the tab
44008      * @return {Roo.TabPanelItem} The created TabPanelItem
44009      */
44010     addTab : function(id, text, content, closable, tpl)
44011     {
44012         var item = new Roo.bootstrap.panel.TabItem({
44013             panel: this,
44014             id : id,
44015             text : text,
44016             closable : closable,
44017             tpl : tpl
44018         });
44019         this.addTabItem(item);
44020         if(content){
44021             item.setContent(content);
44022         }
44023         return item;
44024     },
44025
44026     /**
44027      * Returns the {@link Roo.TabPanelItem} with the specified id/index
44028      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
44029      * @return {Roo.TabPanelItem}
44030      */
44031     getTab : function(id){
44032         return this.items[id];
44033     },
44034
44035     /**
44036      * Hides the {@link Roo.TabPanelItem} with the specified id/index
44037      * @param {String/Number} id The id or index of the TabPanelItem to hide.
44038      */
44039     hideTab : function(id){
44040         var t = this.items[id];
44041         if(!t.isHidden()){
44042            t.setHidden(true);
44043            this.hiddenCount++;
44044            this.autoSizeTabs();
44045         }
44046     },
44047
44048     /**
44049      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
44050      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
44051      */
44052     unhideTab : function(id){
44053         var t = this.items[id];
44054         if(t.isHidden()){
44055            t.setHidden(false);
44056            this.hiddenCount--;
44057            this.autoSizeTabs();
44058         }
44059     },
44060
44061     /**
44062      * Adds an existing {@link Roo.TabPanelItem}.
44063      * @param {Roo.TabPanelItem} item The TabPanelItem to add
44064      */
44065     addTabItem : function(item)
44066     {
44067         this.items[item.id] = item;
44068         this.items.push(item);
44069         this.autoSizeTabs();
44070       //  if(this.resizeTabs){
44071     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
44072   //         this.autoSizeTabs();
44073 //        }else{
44074 //            item.autoSize();
44075        // }
44076     },
44077
44078     /**
44079      * Removes a {@link Roo.TabPanelItem}.
44080      * @param {String/Number} id The id or index of the TabPanelItem to remove.
44081      */
44082     removeTab : function(id){
44083         var items = this.items;
44084         var tab = items[id];
44085         if(!tab) { return; }
44086         var index = items.indexOf(tab);
44087         if(this.active == tab && items.length > 1){
44088             var newTab = this.getNextAvailable(index);
44089             if(newTab) {
44090                 newTab.activate();
44091             }
44092         }
44093         this.stripEl.dom.removeChild(tab.pnode.dom);
44094         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
44095             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
44096         }
44097         items.splice(index, 1);
44098         delete this.items[tab.id];
44099         tab.fireEvent("close", tab);
44100         tab.purgeListeners();
44101         this.autoSizeTabs();
44102     },
44103
44104     getNextAvailable : function(start){
44105         var items = this.items;
44106         var index = start;
44107         // look for a next tab that will slide over to
44108         // replace the one being removed
44109         while(index < items.length){
44110             var item = items[++index];
44111             if(item && !item.isHidden()){
44112                 return item;
44113             }
44114         }
44115         // if one isn't found select the previous tab (on the left)
44116         index = start;
44117         while(index >= 0){
44118             var item = items[--index];
44119             if(item && !item.isHidden()){
44120                 return item;
44121             }
44122         }
44123         return null;
44124     },
44125
44126     /**
44127      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
44128      * @param {String/Number} id The id or index of the TabPanelItem to disable.
44129      */
44130     disableTab : function(id){
44131         var tab = this.items[id];
44132         if(tab && this.active != tab){
44133             tab.disable();
44134         }
44135     },
44136
44137     /**
44138      * Enables a {@link Roo.TabPanelItem} that is disabled.
44139      * @param {String/Number} id The id or index of the TabPanelItem to enable.
44140      */
44141     enableTab : function(id){
44142         var tab = this.items[id];
44143         tab.enable();
44144     },
44145
44146     /**
44147      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
44148      * @param {String/Number} id The id or index of the TabPanelItem to activate.
44149      * @return {Roo.TabPanelItem} The TabPanelItem.
44150      */
44151     activate : function(id)
44152     {
44153         //Roo.log('activite:'  + id);
44154         
44155         var tab = this.items[id];
44156         if(!tab){
44157             return null;
44158         }
44159         if(tab == this.active || tab.disabled){
44160             return tab;
44161         }
44162         var e = {};
44163         this.fireEvent("beforetabchange", this, e, tab);
44164         if(e.cancel !== true && !tab.disabled){
44165             if(this.active){
44166                 this.active.hide();
44167             }
44168             this.active = this.items[id];
44169             this.active.show();
44170             this.fireEvent("tabchange", this, this.active);
44171         }
44172         return tab;
44173     },
44174
44175     /**
44176      * Gets the active {@link Roo.TabPanelItem}.
44177      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
44178      */
44179     getActiveTab : function(){
44180         return this.active;
44181     },
44182
44183     /**
44184      * Updates the tab body element to fit the height of the container element
44185      * for overflow scrolling
44186      * @param {Number} targetHeight (optional) Override the starting height from the elements height
44187      */
44188     syncHeight : function(targetHeight){
44189         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44190         var bm = this.bodyEl.getMargins();
44191         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
44192         this.bodyEl.setHeight(newHeight);
44193         return newHeight;
44194     },
44195
44196     onResize : function(){
44197         if(this.monitorResize){
44198             this.autoSizeTabs();
44199         }
44200     },
44201
44202     /**
44203      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
44204      */
44205     beginUpdate : function(){
44206         this.updating = true;
44207     },
44208
44209     /**
44210      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
44211      */
44212     endUpdate : function(){
44213         this.updating = false;
44214         this.autoSizeTabs();
44215     },
44216
44217     /**
44218      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
44219      */
44220     autoSizeTabs : function()
44221     {
44222         var count = this.items.length;
44223         var vcount = count - this.hiddenCount;
44224         
44225         if (vcount < 2) {
44226             this.stripEl.hide();
44227         } else {
44228             this.stripEl.show();
44229         }
44230         
44231         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
44232             return;
44233         }
44234         
44235         
44236         var w = Math.max(this.el.getWidth() - this.cpad, 10);
44237         var availWidth = Math.floor(w / vcount);
44238         var b = this.stripBody;
44239         if(b.getWidth() > w){
44240             var tabs = this.items;
44241             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
44242             if(availWidth < this.minTabWidth){
44243                 /*if(!this.sleft){    // incomplete scrolling code
44244                     this.createScrollButtons();
44245                 }
44246                 this.showScroll();
44247                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
44248             }
44249         }else{
44250             if(this.currentTabWidth < this.preferredTabWidth){
44251                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
44252             }
44253         }
44254     },
44255
44256     /**
44257      * Returns the number of tabs in this TabPanel.
44258      * @return {Number}
44259      */
44260      getCount : function(){
44261          return this.items.length;
44262      },
44263
44264     /**
44265      * Resizes all the tabs to the passed width
44266      * @param {Number} The new width
44267      */
44268     setTabWidth : function(width){
44269         this.currentTabWidth = width;
44270         for(var i = 0, len = this.items.length; i < len; i++) {
44271                 if(!this.items[i].isHidden()) {
44272                 this.items[i].setWidth(width);
44273             }
44274         }
44275     },
44276
44277     /**
44278      * Destroys this TabPanel
44279      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
44280      */
44281     destroy : function(removeEl){
44282         Roo.EventManager.removeResizeListener(this.onResize, this);
44283         for(var i = 0, len = this.items.length; i < len; i++){
44284             this.items[i].purgeListeners();
44285         }
44286         if(removeEl === true){
44287             this.el.update("");
44288             this.el.remove();
44289         }
44290     },
44291     
44292     createStrip : function(container)
44293     {
44294         var strip = document.createElement("nav");
44295         strip.className = Roo.bootstrap.version == 4 ?
44296             "navbar-light bg-light" : 
44297             "navbar navbar-default"; //"x-tabs-wrap";
44298         container.appendChild(strip);
44299         return strip;
44300     },
44301     
44302     createStripList : function(strip)
44303     {
44304         // div wrapper for retard IE
44305         // returns the "tr" element.
44306         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
44307         //'<div class="x-tabs-strip-wrap">'+
44308           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
44309           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
44310         return strip.firstChild; //.firstChild.firstChild.firstChild;
44311     },
44312     createBody : function(container)
44313     {
44314         var body = document.createElement("div");
44315         Roo.id(body, "tab-body");
44316         //Roo.fly(body).addClass("x-tabs-body");
44317         Roo.fly(body).addClass("tab-content");
44318         container.appendChild(body);
44319         return body;
44320     },
44321     createItemBody :function(bodyEl, id){
44322         var body = Roo.getDom(id);
44323         if(!body){
44324             body = document.createElement("div");
44325             body.id = id;
44326         }
44327         //Roo.fly(body).addClass("x-tabs-item-body");
44328         Roo.fly(body).addClass("tab-pane");
44329          bodyEl.insertBefore(body, bodyEl.firstChild);
44330         return body;
44331     },
44332     /** @private */
44333     createStripElements :  function(stripEl, text, closable, tpl)
44334     {
44335         var td = document.createElement("li"); // was td..
44336         td.className = 'nav-item';
44337         
44338         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
44339         
44340         
44341         stripEl.appendChild(td);
44342         /*if(closable){
44343             td.className = "x-tabs-closable";
44344             if(!this.closeTpl){
44345                 this.closeTpl = new Roo.Template(
44346                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44347                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
44348                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
44349                 );
44350             }
44351             var el = this.closeTpl.overwrite(td, {"text": text});
44352             var close = el.getElementsByTagName("div")[0];
44353             var inner = el.getElementsByTagName("em")[0];
44354             return {"el": el, "close": close, "inner": inner};
44355         } else {
44356         */
44357         // not sure what this is..
44358 //            if(!this.tabTpl){
44359                 //this.tabTpl = new Roo.Template(
44360                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44361                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
44362                 //);
44363 //                this.tabTpl = new Roo.Template(
44364 //                   '<a href="#">' +
44365 //                   '<span unselectable="on"' +
44366 //                            (this.disableTooltips ? '' : ' title="{text}"') +
44367 //                            ' >{text}</span></a>'
44368 //                );
44369 //                
44370 //            }
44371
44372
44373             var template = tpl || this.tabTpl || false;
44374             
44375             if(!template){
44376                 template =  new Roo.Template(
44377                         Roo.bootstrap.version == 4 ? 
44378                             (
44379                                 '<a class="nav-link" href="#" unselectable="on"' +
44380                                      (this.disableTooltips ? '' : ' title="{text}"') +
44381                                      ' >{text}</a>'
44382                             ) : (
44383                                 '<a class="nav-link" href="#">' +
44384                                 '<span unselectable="on"' +
44385                                          (this.disableTooltips ? '' : ' title="{text}"') +
44386                                     ' >{text}</span></a>'
44387                             )
44388                 );
44389             }
44390             
44391             switch (typeof(template)) {
44392                 case 'object' :
44393                     break;
44394                 case 'string' :
44395                     template = new Roo.Template(template);
44396                     break;
44397                 default :
44398                     break;
44399             }
44400             
44401             var el = template.overwrite(td, {"text": text});
44402             
44403             var inner = el.getElementsByTagName("span")[0];
44404             
44405             return {"el": el, "inner": inner};
44406             
44407     }
44408         
44409     
44410 });
44411
44412 /**
44413  * @class Roo.TabPanelItem
44414  * @extends Roo.util.Observable
44415  * Represents an individual item (tab plus body) in a TabPanel.
44416  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
44417  * @param {String} id The id of this TabPanelItem
44418  * @param {String} text The text for the tab of this TabPanelItem
44419  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
44420  */
44421 Roo.bootstrap.panel.TabItem = function(config){
44422     /**
44423      * The {@link Roo.TabPanel} this TabPanelItem belongs to
44424      * @type Roo.TabPanel
44425      */
44426     this.tabPanel = config.panel;
44427     /**
44428      * The id for this TabPanelItem
44429      * @type String
44430      */
44431     this.id = config.id;
44432     /** @private */
44433     this.disabled = false;
44434     /** @private */
44435     this.text = config.text;
44436     /** @private */
44437     this.loaded = false;
44438     this.closable = config.closable;
44439
44440     /**
44441      * The body element for this TabPanelItem.
44442      * @type Roo.Element
44443      */
44444     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
44445     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
44446     this.bodyEl.setStyle("display", "block");
44447     this.bodyEl.setStyle("zoom", "1");
44448     //this.hideAction();
44449
44450     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
44451     /** @private */
44452     this.el = Roo.get(els.el);
44453     this.inner = Roo.get(els.inner, true);
44454      this.textEl = Roo.bootstrap.version == 4 ?
44455         this.el : Roo.get(this.el.dom.firstChild, true);
44456
44457     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
44458     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
44459
44460     
44461 //    this.el.on("mousedown", this.onTabMouseDown, this);
44462     this.el.on("click", this.onTabClick, this);
44463     /** @private */
44464     if(config.closable){
44465         var c = Roo.get(els.close, true);
44466         c.dom.title = this.closeText;
44467         c.addClassOnOver("close-over");
44468         c.on("click", this.closeClick, this);
44469      }
44470
44471     this.addEvents({
44472          /**
44473          * @event activate
44474          * Fires when this tab becomes the active tab.
44475          * @param {Roo.TabPanel} tabPanel The parent TabPanel
44476          * @param {Roo.TabPanelItem} this
44477          */
44478         "activate": true,
44479         /**
44480          * @event beforeclose
44481          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
44482          * @param {Roo.TabPanelItem} this
44483          * @param {Object} e Set cancel to true on this object to cancel the close.
44484          */
44485         "beforeclose": true,
44486         /**
44487          * @event close
44488          * Fires when this tab is closed.
44489          * @param {Roo.TabPanelItem} this
44490          */
44491          "close": true,
44492         /**
44493          * @event deactivate
44494          * Fires when this tab is no longer the active tab.
44495          * @param {Roo.TabPanel} tabPanel The parent TabPanel
44496          * @param {Roo.TabPanelItem} this
44497          */
44498          "deactivate" : true
44499     });
44500     this.hidden = false;
44501
44502     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
44503 };
44504
44505 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
44506            {
44507     purgeListeners : function(){
44508        Roo.util.Observable.prototype.purgeListeners.call(this);
44509        this.el.removeAllListeners();
44510     },
44511     /**
44512      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
44513      */
44514     show : function(){
44515         this.status_node.addClass("active");
44516         this.showAction();
44517         if(Roo.isOpera){
44518             this.tabPanel.stripWrap.repaint();
44519         }
44520         this.fireEvent("activate", this.tabPanel, this);
44521     },
44522
44523     /**
44524      * Returns true if this tab is the active tab.
44525      * @return {Boolean}
44526      */
44527     isActive : function(){
44528         return this.tabPanel.getActiveTab() == this;
44529     },
44530
44531     /**
44532      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
44533      */
44534     hide : function(){
44535         this.status_node.removeClass("active");
44536         this.hideAction();
44537         this.fireEvent("deactivate", this.tabPanel, this);
44538     },
44539
44540     hideAction : function(){
44541         this.bodyEl.hide();
44542         this.bodyEl.setStyle("position", "absolute");
44543         this.bodyEl.setLeft("-20000px");
44544         this.bodyEl.setTop("-20000px");
44545     },
44546
44547     showAction : function(){
44548         this.bodyEl.setStyle("position", "relative");
44549         this.bodyEl.setTop("");
44550         this.bodyEl.setLeft("");
44551         this.bodyEl.show();
44552     },
44553
44554     /**
44555      * Set the tooltip for the tab.
44556      * @param {String} tooltip The tab's tooltip
44557      */
44558     setTooltip : function(text){
44559         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
44560             this.textEl.dom.qtip = text;
44561             this.textEl.dom.removeAttribute('title');
44562         }else{
44563             this.textEl.dom.title = text;
44564         }
44565     },
44566
44567     onTabClick : function(e){
44568         e.preventDefault();
44569         this.tabPanel.activate(this.id);
44570     },
44571
44572     onTabMouseDown : function(e){
44573         e.preventDefault();
44574         this.tabPanel.activate(this.id);
44575     },
44576 /*
44577     getWidth : function(){
44578         return this.inner.getWidth();
44579     },
44580
44581     setWidth : function(width){
44582         var iwidth = width - this.linode.getPadding("lr");
44583         this.inner.setWidth(iwidth);
44584         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
44585         this.linode.setWidth(width);
44586     },
44587 */
44588     /**
44589      * Show or hide the tab
44590      * @param {Boolean} hidden True to hide or false to show.
44591      */
44592     setHidden : function(hidden){
44593         this.hidden = hidden;
44594         this.linode.setStyle("display", hidden ? "none" : "");
44595     },
44596
44597     /**
44598      * Returns true if this tab is "hidden"
44599      * @return {Boolean}
44600      */
44601     isHidden : function(){
44602         return this.hidden;
44603     },
44604
44605     /**
44606      * Returns the text for this tab
44607      * @return {String}
44608      */
44609     getText : function(){
44610         return this.text;
44611     },
44612     /*
44613     autoSize : function(){
44614         //this.el.beginMeasure();
44615         this.textEl.setWidth(1);
44616         /*
44617          *  #2804 [new] Tabs in Roojs
44618          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
44619          */
44620         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
44621         //this.el.endMeasure();
44622     //},
44623
44624     /**
44625      * Sets the text for the tab (Note: this also sets the tooltip text)
44626      * @param {String} text The tab's text and tooltip
44627      */
44628     setText : function(text){
44629         this.text = text;
44630         this.textEl.update(text);
44631         this.setTooltip(text);
44632         //if(!this.tabPanel.resizeTabs){
44633         //    this.autoSize();
44634         //}
44635     },
44636     /**
44637      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
44638      */
44639     activate : function(){
44640         this.tabPanel.activate(this.id);
44641     },
44642
44643     /**
44644      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
44645      */
44646     disable : function(){
44647         if(this.tabPanel.active != this){
44648             this.disabled = true;
44649             this.status_node.addClass("disabled");
44650         }
44651     },
44652
44653     /**
44654      * Enables this TabPanelItem if it was previously disabled.
44655      */
44656     enable : function(){
44657         this.disabled = false;
44658         this.status_node.removeClass("disabled");
44659     },
44660
44661     /**
44662      * Sets the content for this TabPanelItem.
44663      * @param {String} content The content
44664      * @param {Boolean} loadScripts true to look for and load scripts
44665      */
44666     setContent : function(content, loadScripts){
44667         this.bodyEl.update(content, loadScripts);
44668     },
44669
44670     /**
44671      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
44672      * @return {Roo.UpdateManager} The UpdateManager
44673      */
44674     getUpdateManager : function(){
44675         return this.bodyEl.getUpdateManager();
44676     },
44677
44678     /**
44679      * Set a URL to be used to load the content for this TabPanelItem.
44680      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
44681      * @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)
44682      * @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)
44683      * @return {Roo.UpdateManager} The UpdateManager
44684      */
44685     setUrl : function(url, params, loadOnce){
44686         if(this.refreshDelegate){
44687             this.un('activate', this.refreshDelegate);
44688         }
44689         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44690         this.on("activate", this.refreshDelegate);
44691         return this.bodyEl.getUpdateManager();
44692     },
44693
44694     /** @private */
44695     _handleRefresh : function(url, params, loadOnce){
44696         if(!loadOnce || !this.loaded){
44697             var updater = this.bodyEl.getUpdateManager();
44698             updater.update(url, params, this._setLoaded.createDelegate(this));
44699         }
44700     },
44701
44702     /**
44703      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
44704      *   Will fail silently if the setUrl method has not been called.
44705      *   This does not activate the panel, just updates its content.
44706      */
44707     refresh : function(){
44708         if(this.refreshDelegate){
44709            this.loaded = false;
44710            this.refreshDelegate();
44711         }
44712     },
44713
44714     /** @private */
44715     _setLoaded : function(){
44716         this.loaded = true;
44717     },
44718
44719     /** @private */
44720     closeClick : function(e){
44721         var o = {};
44722         e.stopEvent();
44723         this.fireEvent("beforeclose", this, o);
44724         if(o.cancel !== true){
44725             this.tabPanel.removeTab(this.id);
44726         }
44727     },
44728     /**
44729      * The text displayed in the tooltip for the close icon.
44730      * @type String
44731      */
44732     closeText : "Close this tab"
44733 });
44734 /**
44735 *    This script refer to:
44736 *    Title: International Telephone Input
44737 *    Author: Jack O'Connor
44738 *    Code version:  v12.1.12
44739 *    Availability: https://github.com/jackocnr/intl-tel-input.git
44740 **/
44741
44742 Roo.bootstrap.form.PhoneInputData = function() {
44743     var d = [
44744       [
44745         "Afghanistan (‫افغانستان‬‎)",
44746         "af",
44747         "93"
44748       ],
44749       [
44750         "Albania (Shqipëri)",
44751         "al",
44752         "355"
44753       ],
44754       [
44755         "Algeria (‫الجزائر‬‎)",
44756         "dz",
44757         "213"
44758       ],
44759       [
44760         "American Samoa",
44761         "as",
44762         "1684"
44763       ],
44764       [
44765         "Andorra",
44766         "ad",
44767         "376"
44768       ],
44769       [
44770         "Angola",
44771         "ao",
44772         "244"
44773       ],
44774       [
44775         "Anguilla",
44776         "ai",
44777         "1264"
44778       ],
44779       [
44780         "Antigua and Barbuda",
44781         "ag",
44782         "1268"
44783       ],
44784       [
44785         "Argentina",
44786         "ar",
44787         "54"
44788       ],
44789       [
44790         "Armenia (Հայաստան)",
44791         "am",
44792         "374"
44793       ],
44794       [
44795         "Aruba",
44796         "aw",
44797         "297"
44798       ],
44799       [
44800         "Australia",
44801         "au",
44802         "61",
44803         0
44804       ],
44805       [
44806         "Austria (Österreich)",
44807         "at",
44808         "43"
44809       ],
44810       [
44811         "Azerbaijan (Azərbaycan)",
44812         "az",
44813         "994"
44814       ],
44815       [
44816         "Bahamas",
44817         "bs",
44818         "1242"
44819       ],
44820       [
44821         "Bahrain (‫البحرين‬‎)",
44822         "bh",
44823         "973"
44824       ],
44825       [
44826         "Bangladesh (বাংলাদেশ)",
44827         "bd",
44828         "880"
44829       ],
44830       [
44831         "Barbados",
44832         "bb",
44833         "1246"
44834       ],
44835       [
44836         "Belarus (Беларусь)",
44837         "by",
44838         "375"
44839       ],
44840       [
44841         "Belgium (België)",
44842         "be",
44843         "32"
44844       ],
44845       [
44846         "Belize",
44847         "bz",
44848         "501"
44849       ],
44850       [
44851         "Benin (Bénin)",
44852         "bj",
44853         "229"
44854       ],
44855       [
44856         "Bermuda",
44857         "bm",
44858         "1441"
44859       ],
44860       [
44861         "Bhutan (འབྲུག)",
44862         "bt",
44863         "975"
44864       ],
44865       [
44866         "Bolivia",
44867         "bo",
44868         "591"
44869       ],
44870       [
44871         "Bosnia and Herzegovina (Босна и Херцеговина)",
44872         "ba",
44873         "387"
44874       ],
44875       [
44876         "Botswana",
44877         "bw",
44878         "267"
44879       ],
44880       [
44881         "Brazil (Brasil)",
44882         "br",
44883         "55"
44884       ],
44885       [
44886         "British Indian Ocean Territory",
44887         "io",
44888         "246"
44889       ],
44890       [
44891         "British Virgin Islands",
44892         "vg",
44893         "1284"
44894       ],
44895       [
44896         "Brunei",
44897         "bn",
44898         "673"
44899       ],
44900       [
44901         "Bulgaria (България)",
44902         "bg",
44903         "359"
44904       ],
44905       [
44906         "Burkina Faso",
44907         "bf",
44908         "226"
44909       ],
44910       [
44911         "Burundi (Uburundi)",
44912         "bi",
44913         "257"
44914       ],
44915       [
44916         "Cambodia (កម្ពុជា)",
44917         "kh",
44918         "855"
44919       ],
44920       [
44921         "Cameroon (Cameroun)",
44922         "cm",
44923         "237"
44924       ],
44925       [
44926         "Canada",
44927         "ca",
44928         "1",
44929         1,
44930         ["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"]
44931       ],
44932       [
44933         "Cape Verde (Kabu Verdi)",
44934         "cv",
44935         "238"
44936       ],
44937       [
44938         "Caribbean Netherlands",
44939         "bq",
44940         "599",
44941         1
44942       ],
44943       [
44944         "Cayman Islands",
44945         "ky",
44946         "1345"
44947       ],
44948       [
44949         "Central African Republic (République centrafricaine)",
44950         "cf",
44951         "236"
44952       ],
44953       [
44954         "Chad (Tchad)",
44955         "td",
44956         "235"
44957       ],
44958       [
44959         "Chile",
44960         "cl",
44961         "56"
44962       ],
44963       [
44964         "China (中国)",
44965         "cn",
44966         "86"
44967       ],
44968       [
44969         "Christmas Island",
44970         "cx",
44971         "61",
44972         2
44973       ],
44974       [
44975         "Cocos (Keeling) Islands",
44976         "cc",
44977         "61",
44978         1
44979       ],
44980       [
44981         "Colombia",
44982         "co",
44983         "57"
44984       ],
44985       [
44986         "Comoros (‫جزر القمر‬‎)",
44987         "km",
44988         "269"
44989       ],
44990       [
44991         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
44992         "cd",
44993         "243"
44994       ],
44995       [
44996         "Congo (Republic) (Congo-Brazzaville)",
44997         "cg",
44998         "242"
44999       ],
45000       [
45001         "Cook Islands",
45002         "ck",
45003         "682"
45004       ],
45005       [
45006         "Costa Rica",
45007         "cr",
45008         "506"
45009       ],
45010       [
45011         "Côte d’Ivoire",
45012         "ci",
45013         "225"
45014       ],
45015       [
45016         "Croatia (Hrvatska)",
45017         "hr",
45018         "385"
45019       ],
45020       [
45021         "Cuba",
45022         "cu",
45023         "53"
45024       ],
45025       [
45026         "Curaçao",
45027         "cw",
45028         "599",
45029         0
45030       ],
45031       [
45032         "Cyprus (Κύπρος)",
45033         "cy",
45034         "357"
45035       ],
45036       [
45037         "Czech Republic (Česká republika)",
45038         "cz",
45039         "420"
45040       ],
45041       [
45042         "Denmark (Danmark)",
45043         "dk",
45044         "45"
45045       ],
45046       [
45047         "Djibouti",
45048         "dj",
45049         "253"
45050       ],
45051       [
45052         "Dominica",
45053         "dm",
45054         "1767"
45055       ],
45056       [
45057         "Dominican Republic (República Dominicana)",
45058         "do",
45059         "1",
45060         2,
45061         ["809", "829", "849"]
45062       ],
45063       [
45064         "Ecuador",
45065         "ec",
45066         "593"
45067       ],
45068       [
45069         "Egypt (‫مصر‬‎)",
45070         "eg",
45071         "20"
45072       ],
45073       [
45074         "El Salvador",
45075         "sv",
45076         "503"
45077       ],
45078       [
45079         "Equatorial Guinea (Guinea Ecuatorial)",
45080         "gq",
45081         "240"
45082       ],
45083       [
45084         "Eritrea",
45085         "er",
45086         "291"
45087       ],
45088       [
45089         "Estonia (Eesti)",
45090         "ee",
45091         "372"
45092       ],
45093       [
45094         "Ethiopia",
45095         "et",
45096         "251"
45097       ],
45098       [
45099         "Falkland Islands (Islas Malvinas)",
45100         "fk",
45101         "500"
45102       ],
45103       [
45104         "Faroe Islands (Føroyar)",
45105         "fo",
45106         "298"
45107       ],
45108       [
45109         "Fiji",
45110         "fj",
45111         "679"
45112       ],
45113       [
45114         "Finland (Suomi)",
45115         "fi",
45116         "358",
45117         0
45118       ],
45119       [
45120         "France",
45121         "fr",
45122         "33"
45123       ],
45124       [
45125         "French Guiana (Guyane française)",
45126         "gf",
45127         "594"
45128       ],
45129       [
45130         "French Polynesia (Polynésie française)",
45131         "pf",
45132         "689"
45133       ],
45134       [
45135         "Gabon",
45136         "ga",
45137         "241"
45138       ],
45139       [
45140         "Gambia",
45141         "gm",
45142         "220"
45143       ],
45144       [
45145         "Georgia (საქართველო)",
45146         "ge",
45147         "995"
45148       ],
45149       [
45150         "Germany (Deutschland)",
45151         "de",
45152         "49"
45153       ],
45154       [
45155         "Ghana (Gaana)",
45156         "gh",
45157         "233"
45158       ],
45159       [
45160         "Gibraltar",
45161         "gi",
45162         "350"
45163       ],
45164       [
45165         "Greece (Ελλάδα)",
45166         "gr",
45167         "30"
45168       ],
45169       [
45170         "Greenland (Kalaallit Nunaat)",
45171         "gl",
45172         "299"
45173       ],
45174       [
45175         "Grenada",
45176         "gd",
45177         "1473"
45178       ],
45179       [
45180         "Guadeloupe",
45181         "gp",
45182         "590",
45183         0
45184       ],
45185       [
45186         "Guam",
45187         "gu",
45188         "1671"
45189       ],
45190       [
45191         "Guatemala",
45192         "gt",
45193         "502"
45194       ],
45195       [
45196         "Guernsey",
45197         "gg",
45198         "44",
45199         1
45200       ],
45201       [
45202         "Guinea (Guinée)",
45203         "gn",
45204         "224"
45205       ],
45206       [
45207         "Guinea-Bissau (Guiné Bissau)",
45208         "gw",
45209         "245"
45210       ],
45211       [
45212         "Guyana",
45213         "gy",
45214         "592"
45215       ],
45216       [
45217         "Haiti",
45218         "ht",
45219         "509"
45220       ],
45221       [
45222         "Honduras",
45223         "hn",
45224         "504"
45225       ],
45226       [
45227         "Hong Kong (香港)",
45228         "hk",
45229         "852"
45230       ],
45231       [
45232         "Hungary (Magyarország)",
45233         "hu",
45234         "36"
45235       ],
45236       [
45237         "Iceland (Ísland)",
45238         "is",
45239         "354"
45240       ],
45241       [
45242         "India (भारत)",
45243         "in",
45244         "91"
45245       ],
45246       [
45247         "Indonesia",
45248         "id",
45249         "62"
45250       ],
45251       [
45252         "Iran (‫ایران‬‎)",
45253         "ir",
45254         "98"
45255       ],
45256       [
45257         "Iraq (‫العراق‬‎)",
45258         "iq",
45259         "964"
45260       ],
45261       [
45262         "Ireland",
45263         "ie",
45264         "353"
45265       ],
45266       [
45267         "Isle of Man",
45268         "im",
45269         "44",
45270         2
45271       ],
45272       [
45273         "Israel (‫ישראל‬‎)",
45274         "il",
45275         "972"
45276       ],
45277       [
45278         "Italy (Italia)",
45279         "it",
45280         "39",
45281         0
45282       ],
45283       [
45284         "Jamaica",
45285         "jm",
45286         "1876"
45287       ],
45288       [
45289         "Japan (日本)",
45290         "jp",
45291         "81"
45292       ],
45293       [
45294         "Jersey",
45295         "je",
45296         "44",
45297         3
45298       ],
45299       [
45300         "Jordan (‫الأردن‬‎)",
45301         "jo",
45302         "962"
45303       ],
45304       [
45305         "Kazakhstan (Казахстан)",
45306         "kz",
45307         "7",
45308         1
45309       ],
45310       [
45311         "Kenya",
45312         "ke",
45313         "254"
45314       ],
45315       [
45316         "Kiribati",
45317         "ki",
45318         "686"
45319       ],
45320       [
45321         "Kosovo",
45322         "xk",
45323         "383"
45324       ],
45325       [
45326         "Kuwait (‫الكويت‬‎)",
45327         "kw",
45328         "965"
45329       ],
45330       [
45331         "Kyrgyzstan (Кыргызстан)",
45332         "kg",
45333         "996"
45334       ],
45335       [
45336         "Laos (ລາວ)",
45337         "la",
45338         "856"
45339       ],
45340       [
45341         "Latvia (Latvija)",
45342         "lv",
45343         "371"
45344       ],
45345       [
45346         "Lebanon (‫لبنان‬‎)",
45347         "lb",
45348         "961"
45349       ],
45350       [
45351         "Lesotho",
45352         "ls",
45353         "266"
45354       ],
45355       [
45356         "Liberia",
45357         "lr",
45358         "231"
45359       ],
45360       [
45361         "Libya (‫ليبيا‬‎)",
45362         "ly",
45363         "218"
45364       ],
45365       [
45366         "Liechtenstein",
45367         "li",
45368         "423"
45369       ],
45370       [
45371         "Lithuania (Lietuva)",
45372         "lt",
45373         "370"
45374       ],
45375       [
45376         "Luxembourg",
45377         "lu",
45378         "352"
45379       ],
45380       [
45381         "Macau (澳門)",
45382         "mo",
45383         "853"
45384       ],
45385       [
45386         "Macedonia (FYROM) (Македонија)",
45387         "mk",
45388         "389"
45389       ],
45390       [
45391         "Madagascar (Madagasikara)",
45392         "mg",
45393         "261"
45394       ],
45395       [
45396         "Malawi",
45397         "mw",
45398         "265"
45399       ],
45400       [
45401         "Malaysia",
45402         "my",
45403         "60"
45404       ],
45405       [
45406         "Maldives",
45407         "mv",
45408         "960"
45409       ],
45410       [
45411         "Mali",
45412         "ml",
45413         "223"
45414       ],
45415       [
45416         "Malta",
45417         "mt",
45418         "356"
45419       ],
45420       [
45421         "Marshall Islands",
45422         "mh",
45423         "692"
45424       ],
45425       [
45426         "Martinique",
45427         "mq",
45428         "596"
45429       ],
45430       [
45431         "Mauritania (‫موريتانيا‬‎)",
45432         "mr",
45433         "222"
45434       ],
45435       [
45436         "Mauritius (Moris)",
45437         "mu",
45438         "230"
45439       ],
45440       [
45441         "Mayotte",
45442         "yt",
45443         "262",
45444         1
45445       ],
45446       [
45447         "Mexico (México)",
45448         "mx",
45449         "52"
45450       ],
45451       [
45452         "Micronesia",
45453         "fm",
45454         "691"
45455       ],
45456       [
45457         "Moldova (Republica Moldova)",
45458         "md",
45459         "373"
45460       ],
45461       [
45462         "Monaco",
45463         "mc",
45464         "377"
45465       ],
45466       [
45467         "Mongolia (Монгол)",
45468         "mn",
45469         "976"
45470       ],
45471       [
45472         "Montenegro (Crna Gora)",
45473         "me",
45474         "382"
45475       ],
45476       [
45477         "Montserrat",
45478         "ms",
45479         "1664"
45480       ],
45481       [
45482         "Morocco (‫المغرب‬‎)",
45483         "ma",
45484         "212",
45485         0
45486       ],
45487       [
45488         "Mozambique (Moçambique)",
45489         "mz",
45490         "258"
45491       ],
45492       [
45493         "Myanmar (Burma) (မြန်မာ)",
45494         "mm",
45495         "95"
45496       ],
45497       [
45498         "Namibia (Namibië)",
45499         "na",
45500         "264"
45501       ],
45502       [
45503         "Nauru",
45504         "nr",
45505         "674"
45506       ],
45507       [
45508         "Nepal (नेपाल)",
45509         "np",
45510         "977"
45511       ],
45512       [
45513         "Netherlands (Nederland)",
45514         "nl",
45515         "31"
45516       ],
45517       [
45518         "New Caledonia (Nouvelle-Calédonie)",
45519         "nc",
45520         "687"
45521       ],
45522       [
45523         "New Zealand",
45524         "nz",
45525         "64"
45526       ],
45527       [
45528         "Nicaragua",
45529         "ni",
45530         "505"
45531       ],
45532       [
45533         "Niger (Nijar)",
45534         "ne",
45535         "227"
45536       ],
45537       [
45538         "Nigeria",
45539         "ng",
45540         "234"
45541       ],
45542       [
45543         "Niue",
45544         "nu",
45545         "683"
45546       ],
45547       [
45548         "Norfolk Island",
45549         "nf",
45550         "672"
45551       ],
45552       [
45553         "North Korea (조선 민주주의 인민 공화국)",
45554         "kp",
45555         "850"
45556       ],
45557       [
45558         "Northern Mariana Islands",
45559         "mp",
45560         "1670"
45561       ],
45562       [
45563         "Norway (Norge)",
45564         "no",
45565         "47",
45566         0
45567       ],
45568       [
45569         "Oman (‫عُمان‬‎)",
45570         "om",
45571         "968"
45572       ],
45573       [
45574         "Pakistan (‫پاکستان‬‎)",
45575         "pk",
45576         "92"
45577       ],
45578       [
45579         "Palau",
45580         "pw",
45581         "680"
45582       ],
45583       [
45584         "Palestine (‫فلسطين‬‎)",
45585         "ps",
45586         "970"
45587       ],
45588       [
45589         "Panama (Panamá)",
45590         "pa",
45591         "507"
45592       ],
45593       [
45594         "Papua New Guinea",
45595         "pg",
45596         "675"
45597       ],
45598       [
45599         "Paraguay",
45600         "py",
45601         "595"
45602       ],
45603       [
45604         "Peru (Perú)",
45605         "pe",
45606         "51"
45607       ],
45608       [
45609         "Philippines",
45610         "ph",
45611         "63"
45612       ],
45613       [
45614         "Poland (Polska)",
45615         "pl",
45616         "48"
45617       ],
45618       [
45619         "Portugal",
45620         "pt",
45621         "351"
45622       ],
45623       [
45624         "Puerto Rico",
45625         "pr",
45626         "1",
45627         3,
45628         ["787", "939"]
45629       ],
45630       [
45631         "Qatar (‫قطر‬‎)",
45632         "qa",
45633         "974"
45634       ],
45635       [
45636         "Réunion (La Réunion)",
45637         "re",
45638         "262",
45639         0
45640       ],
45641       [
45642         "Romania (România)",
45643         "ro",
45644         "40"
45645       ],
45646       [
45647         "Russia (Россия)",
45648         "ru",
45649         "7",
45650         0
45651       ],
45652       [
45653         "Rwanda",
45654         "rw",
45655         "250"
45656       ],
45657       [
45658         "Saint Barthélemy",
45659         "bl",
45660         "590",
45661         1
45662       ],
45663       [
45664         "Saint Helena",
45665         "sh",
45666         "290"
45667       ],
45668       [
45669         "Saint Kitts and Nevis",
45670         "kn",
45671         "1869"
45672       ],
45673       [
45674         "Saint Lucia",
45675         "lc",
45676         "1758"
45677       ],
45678       [
45679         "Saint Martin (Saint-Martin (partie française))",
45680         "mf",
45681         "590",
45682         2
45683       ],
45684       [
45685         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
45686         "pm",
45687         "508"
45688       ],
45689       [
45690         "Saint Vincent and the Grenadines",
45691         "vc",
45692         "1784"
45693       ],
45694       [
45695         "Samoa",
45696         "ws",
45697         "685"
45698       ],
45699       [
45700         "San Marino",
45701         "sm",
45702         "378"
45703       ],
45704       [
45705         "São Tomé and Príncipe (São Tomé e Príncipe)",
45706         "st",
45707         "239"
45708       ],
45709       [
45710         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
45711         "sa",
45712         "966"
45713       ],
45714       [
45715         "Senegal (Sénégal)",
45716         "sn",
45717         "221"
45718       ],
45719       [
45720         "Serbia (Србија)",
45721         "rs",
45722         "381"
45723       ],
45724       [
45725         "Seychelles",
45726         "sc",
45727         "248"
45728       ],
45729       [
45730         "Sierra Leone",
45731         "sl",
45732         "232"
45733       ],
45734       [
45735         "Singapore",
45736         "sg",
45737         "65"
45738       ],
45739       [
45740         "Sint Maarten",
45741         "sx",
45742         "1721"
45743       ],
45744       [
45745         "Slovakia (Slovensko)",
45746         "sk",
45747         "421"
45748       ],
45749       [
45750         "Slovenia (Slovenija)",
45751         "si",
45752         "386"
45753       ],
45754       [
45755         "Solomon Islands",
45756         "sb",
45757         "677"
45758       ],
45759       [
45760         "Somalia (Soomaaliya)",
45761         "so",
45762         "252"
45763       ],
45764       [
45765         "South Africa",
45766         "za",
45767         "27"
45768       ],
45769       [
45770         "South Korea (대한민국)",
45771         "kr",
45772         "82"
45773       ],
45774       [
45775         "South Sudan (‫جنوب السودان‬‎)",
45776         "ss",
45777         "211"
45778       ],
45779       [
45780         "Spain (España)",
45781         "es",
45782         "34"
45783       ],
45784       [
45785         "Sri Lanka (ශ්‍රී ලංකාව)",
45786         "lk",
45787         "94"
45788       ],
45789       [
45790         "Sudan (‫السودان‬‎)",
45791         "sd",
45792         "249"
45793       ],
45794       [
45795         "Suriname",
45796         "sr",
45797         "597"
45798       ],
45799       [
45800         "Svalbard and Jan Mayen",
45801         "sj",
45802         "47",
45803         1
45804       ],
45805       [
45806         "Swaziland",
45807         "sz",
45808         "268"
45809       ],
45810       [
45811         "Sweden (Sverige)",
45812         "se",
45813         "46"
45814       ],
45815       [
45816         "Switzerland (Schweiz)",
45817         "ch",
45818         "41"
45819       ],
45820       [
45821         "Syria (‫سوريا‬‎)",
45822         "sy",
45823         "963"
45824       ],
45825       [
45826         "Taiwan (台灣)",
45827         "tw",
45828         "886"
45829       ],
45830       [
45831         "Tajikistan",
45832         "tj",
45833         "992"
45834       ],
45835       [
45836         "Tanzania",
45837         "tz",
45838         "255"
45839       ],
45840       [
45841         "Thailand (ไทย)",
45842         "th",
45843         "66"
45844       ],
45845       [
45846         "Timor-Leste",
45847         "tl",
45848         "670"
45849       ],
45850       [
45851         "Togo",
45852         "tg",
45853         "228"
45854       ],
45855       [
45856         "Tokelau",
45857         "tk",
45858         "690"
45859       ],
45860       [
45861         "Tonga",
45862         "to",
45863         "676"
45864       ],
45865       [
45866         "Trinidad and Tobago",
45867         "tt",
45868         "1868"
45869       ],
45870       [
45871         "Tunisia (‫تونس‬‎)",
45872         "tn",
45873         "216"
45874       ],
45875       [
45876         "Turkey (Türkiye)",
45877         "tr",
45878         "90"
45879       ],
45880       [
45881         "Turkmenistan",
45882         "tm",
45883         "993"
45884       ],
45885       [
45886         "Turks and Caicos Islands",
45887         "tc",
45888         "1649"
45889       ],
45890       [
45891         "Tuvalu",
45892         "tv",
45893         "688"
45894       ],
45895       [
45896         "U.S. Virgin Islands",
45897         "vi",
45898         "1340"
45899       ],
45900       [
45901         "Uganda",
45902         "ug",
45903         "256"
45904       ],
45905       [
45906         "Ukraine (Україна)",
45907         "ua",
45908         "380"
45909       ],
45910       [
45911         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
45912         "ae",
45913         "971"
45914       ],
45915       [
45916         "United Kingdom",
45917         "gb",
45918         "44",
45919         0
45920       ],
45921       [
45922         "United States",
45923         "us",
45924         "1",
45925         0
45926       ],
45927       [
45928         "Uruguay",
45929         "uy",
45930         "598"
45931       ],
45932       [
45933         "Uzbekistan (Oʻzbekiston)",
45934         "uz",
45935         "998"
45936       ],
45937       [
45938         "Vanuatu",
45939         "vu",
45940         "678"
45941       ],
45942       [
45943         "Vatican City (Città del Vaticano)",
45944         "va",
45945         "39",
45946         1
45947       ],
45948       [
45949         "Venezuela",
45950         "ve",
45951         "58"
45952       ],
45953       [
45954         "Vietnam (Việt Nam)",
45955         "vn",
45956         "84"
45957       ],
45958       [
45959         "Wallis and Futuna (Wallis-et-Futuna)",
45960         "wf",
45961         "681"
45962       ],
45963       [
45964         "Western Sahara (‫الصحراء الغربية‬‎)",
45965         "eh",
45966         "212",
45967         1
45968       ],
45969       [
45970         "Yemen (‫اليمن‬‎)",
45971         "ye",
45972         "967"
45973       ],
45974       [
45975         "Zambia",
45976         "zm",
45977         "260"
45978       ],
45979       [
45980         "Zimbabwe",
45981         "zw",
45982         "263"
45983       ],
45984       [
45985         "Åland Islands",
45986         "ax",
45987         "358",
45988         1
45989       ]
45990   ];
45991   
45992   return d;
45993 }/**
45994 *    This script refer to:
45995 *    Title: International Telephone Input
45996 *    Author: Jack O'Connor
45997 *    Code version:  v12.1.12
45998 *    Availability: https://github.com/jackocnr/intl-tel-input.git
45999 **/
46000
46001 /**
46002  * @class Roo.bootstrap.form.PhoneInput
46003  * @extends Roo.bootstrap.form.TriggerField
46004  * An input with International dial-code selection
46005  
46006  * @cfg {String} defaultDialCode default '+852'
46007  * @cfg {Array} preferedCountries default []
46008   
46009  * @constructor
46010  * Create a new PhoneInput.
46011  * @param {Object} config Configuration options
46012  */
46013
46014 Roo.bootstrap.form.PhoneInput = function(config) {
46015     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
46016 };
46017
46018 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
46019         /**
46020         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
46021         */
46022         listWidth: undefined,
46023         
46024         selectedClass: 'active',
46025         
46026         invalidClass : "has-warning",
46027         
46028         validClass: 'has-success',
46029         
46030         allowed: '0123456789',
46031         
46032         max_length: 15,
46033         
46034         /**
46035          * @cfg {String} defaultDialCode The default dial code when initializing the input
46036          */
46037         defaultDialCode: '+852',
46038         
46039         /**
46040          * @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
46041          */
46042         preferedCountries: false,
46043         
46044         getAutoCreate : function()
46045         {
46046             var data = Roo.bootstrap.form.PhoneInputData();
46047             var align = this.labelAlign || this.parentLabelAlign();
46048             var id = Roo.id();
46049             
46050             this.allCountries = [];
46051             this.dialCodeMapping = [];
46052             
46053             for (var i = 0; i < data.length; i++) {
46054               var c = data[i];
46055               this.allCountries[i] = {
46056                 name: c[0],
46057                 iso2: c[1],
46058                 dialCode: c[2],
46059                 priority: c[3] || 0,
46060                 areaCodes: c[4] || null
46061               };
46062               this.dialCodeMapping[c[2]] = {
46063                   name: c[0],
46064                   iso2: c[1],
46065                   priority: c[3] || 0,
46066                   areaCodes: c[4] || null
46067               };
46068             }
46069             
46070             var cfg = {
46071                 cls: 'form-group',
46072                 cn: []
46073             };
46074             
46075             var input =  {
46076                 tag: 'input',
46077                 id : id,
46078                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
46079                 maxlength: this.max_length,
46080                 cls : 'form-control tel-input',
46081                 autocomplete: 'new-password'
46082             };
46083             
46084             var hiddenInput = {
46085                 tag: 'input',
46086                 type: 'hidden',
46087                 cls: 'hidden-tel-input'
46088             };
46089             
46090             if (this.name) {
46091                 hiddenInput.name = this.name;
46092             }
46093             
46094             if (this.disabled) {
46095                 input.disabled = true;
46096             }
46097             
46098             var flag_container = {
46099                 tag: 'div',
46100                 cls: 'flag-box',
46101                 cn: [
46102                     {
46103                         tag: 'div',
46104                         cls: 'flag'
46105                     },
46106                     {
46107                         tag: 'div',
46108                         cls: 'caret'
46109                     }
46110                 ]
46111             };
46112             
46113             var box = {
46114                 tag: 'div',
46115                 cls: this.hasFeedback ? 'has-feedback' : '',
46116                 cn: [
46117                     hiddenInput,
46118                     input,
46119                     {
46120                         tag: 'input',
46121                         cls: 'dial-code-holder',
46122                         disabled: true
46123                     }
46124                 ]
46125             };
46126             
46127             var container = {
46128                 cls: 'roo-select2-container input-group',
46129                 cn: [
46130                     flag_container,
46131                     box
46132                 ]
46133             };
46134             
46135             if (this.fieldLabel.length) {
46136                 var indicator = {
46137                     tag: 'i',
46138                     tooltip: 'This field is required'
46139                 };
46140                 
46141                 var label = {
46142                     tag: 'label',
46143                     'for':  id,
46144                     cls: 'control-label',
46145                     cn: []
46146                 };
46147                 
46148                 var label_text = {
46149                     tag: 'span',
46150                     html: this.fieldLabel
46151                 };
46152                 
46153                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46154                 label.cn = [
46155                     indicator,
46156                     label_text
46157                 ];
46158                 
46159                 if(this.indicatorpos == 'right') {
46160                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46161                     label.cn = [
46162                         label_text,
46163                         indicator
46164                     ];
46165                 }
46166                 
46167                 if(align == 'left') {
46168                     container = {
46169                         tag: 'div',
46170                         cn: [
46171                             container
46172                         ]
46173                     };
46174                     
46175                     if(this.labelWidth > 12){
46176                         label.style = "width: " + this.labelWidth + 'px';
46177                     }
46178                     if(this.labelWidth < 13 && this.labelmd == 0){
46179                         this.labelmd = this.labelWidth;
46180                     }
46181                     if(this.labellg > 0){
46182                         label.cls += ' col-lg-' + this.labellg;
46183                         input.cls += ' col-lg-' + (12 - this.labellg);
46184                     }
46185                     if(this.labelmd > 0){
46186                         label.cls += ' col-md-' + this.labelmd;
46187                         container.cls += ' col-md-' + (12 - this.labelmd);
46188                     }
46189                     if(this.labelsm > 0){
46190                         label.cls += ' col-sm-' + this.labelsm;
46191                         container.cls += ' col-sm-' + (12 - this.labelsm);
46192                     }
46193                     if(this.labelxs > 0){
46194                         label.cls += ' col-xs-' + this.labelxs;
46195                         container.cls += ' col-xs-' + (12 - this.labelxs);
46196                     }
46197                 }
46198             }
46199             
46200             cfg.cn = [
46201                 label,
46202                 container
46203             ];
46204             
46205             var settings = this;
46206             
46207             ['xs','sm','md','lg'].map(function(size){
46208                 if (settings[size]) {
46209                     cfg.cls += ' col-' + size + '-' + settings[size];
46210                 }
46211             });
46212             
46213             this.store = new Roo.data.Store({
46214                 proxy : new Roo.data.MemoryProxy({}),
46215                 reader : new Roo.data.JsonReader({
46216                     fields : [
46217                         {
46218                             'name' : 'name',
46219                             'type' : 'string'
46220                         },
46221                         {
46222                             'name' : 'iso2',
46223                             'type' : 'string'
46224                         },
46225                         {
46226                             'name' : 'dialCode',
46227                             'type' : 'string'
46228                         },
46229                         {
46230                             'name' : 'priority',
46231                             'type' : 'string'
46232                         },
46233                         {
46234                             'name' : 'areaCodes',
46235                             'type' : 'string'
46236                         }
46237                     ]
46238                 })
46239             });
46240             
46241             if(!this.preferedCountries) {
46242                 this.preferedCountries = [
46243                     'hk',
46244                     'gb',
46245                     'us'
46246                 ];
46247             }
46248             
46249             var p = this.preferedCountries.reverse();
46250             
46251             if(p) {
46252                 for (var i = 0; i < p.length; i++) {
46253                     for (var j = 0; j < this.allCountries.length; j++) {
46254                         if(this.allCountries[j].iso2 == p[i]) {
46255                             var t = this.allCountries[j];
46256                             this.allCountries.splice(j,1);
46257                             this.allCountries.unshift(t);
46258                         }
46259                     } 
46260                 }
46261             }
46262             
46263             this.store.proxy.data = {
46264                 success: true,
46265                 data: this.allCountries
46266             };
46267             
46268             return cfg;
46269         },
46270         
46271         initEvents : function()
46272         {
46273             this.createList();
46274             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
46275             
46276             this.indicator = this.indicatorEl();
46277             this.flag = this.flagEl();
46278             this.dialCodeHolder = this.dialCodeHolderEl();
46279             
46280             this.trigger = this.el.select('div.flag-box',true).first();
46281             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
46282             
46283             var _this = this;
46284             
46285             (function(){
46286                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
46287                 _this.list.setWidth(lw);
46288             }).defer(100);
46289             
46290             this.list.on('mouseover', this.onViewOver, this);
46291             this.list.on('mousemove', this.onViewMove, this);
46292             this.inputEl().on("keyup", this.onKeyUp, this);
46293             this.inputEl().on("keypress", this.onKeyPress, this);
46294             
46295             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
46296
46297             this.view = new Roo.View(this.list, this.tpl, {
46298                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
46299             });
46300             
46301             this.view.on('click', this.onViewClick, this);
46302             this.setValue(this.defaultDialCode);
46303         },
46304         
46305         onTriggerClick : function(e)
46306         {
46307             Roo.log('trigger click');
46308             if(this.disabled){
46309                 return;
46310             }
46311             
46312             if(this.isExpanded()){
46313                 this.collapse();
46314                 this.hasFocus = false;
46315             }else {
46316                 this.store.load({});
46317                 this.hasFocus = true;
46318                 this.expand();
46319             }
46320         },
46321         
46322         isExpanded : function()
46323         {
46324             return this.list.isVisible();
46325         },
46326         
46327         collapse : function()
46328         {
46329             if(!this.isExpanded()){
46330                 return;
46331             }
46332             this.list.hide();
46333             Roo.get(document).un('mousedown', this.collapseIf, this);
46334             Roo.get(document).un('mousewheel', this.collapseIf, this);
46335             this.fireEvent('collapse', this);
46336             this.validate();
46337         },
46338         
46339         expand : function()
46340         {
46341             Roo.log('expand');
46342
46343             if(this.isExpanded() || !this.hasFocus){
46344                 return;
46345             }
46346             
46347             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
46348             this.list.setWidth(lw);
46349             
46350             this.list.show();
46351             this.restrictHeight();
46352             
46353             Roo.get(document).on('mousedown', this.collapseIf, this);
46354             Roo.get(document).on('mousewheel', this.collapseIf, this);
46355             
46356             this.fireEvent('expand', this);
46357         },
46358         
46359         restrictHeight : function()
46360         {
46361             this.list.alignTo(this.inputEl(), this.listAlign);
46362             this.list.alignTo(this.inputEl(), this.listAlign);
46363         },
46364         
46365         onViewOver : function(e, t)
46366         {
46367             if(this.inKeyMode){
46368                 return;
46369             }
46370             var item = this.view.findItemFromChild(t);
46371             
46372             if(item){
46373                 var index = this.view.indexOf(item);
46374                 this.select(index, false);
46375             }
46376         },
46377
46378         // private
46379         onViewClick : function(view, doFocus, el, e)
46380         {
46381             var index = this.view.getSelectedIndexes()[0];
46382             
46383             var r = this.store.getAt(index);
46384             
46385             if(r){
46386                 this.onSelect(r, index);
46387             }
46388             if(doFocus !== false && !this.blockFocus){
46389                 this.inputEl().focus();
46390             }
46391         },
46392         
46393         onViewMove : function(e, t)
46394         {
46395             this.inKeyMode = false;
46396         },
46397         
46398         select : function(index, scrollIntoView)
46399         {
46400             this.selectedIndex = index;
46401             this.view.select(index);
46402             if(scrollIntoView !== false){
46403                 var el = this.view.getNode(index);
46404                 if(el){
46405                     this.list.scrollChildIntoView(el, false);
46406                 }
46407             }
46408         },
46409         
46410         createList : function()
46411         {
46412             this.list = Roo.get(document.body).createChild({
46413                 tag: 'ul',
46414                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
46415                 style: 'display:none'
46416             });
46417             
46418             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
46419         },
46420         
46421         collapseIf : function(e)
46422         {
46423             var in_combo  = e.within(this.el);
46424             var in_list =  e.within(this.list);
46425             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
46426             
46427             if (in_combo || in_list || is_list) {
46428                 return;
46429             }
46430             this.collapse();
46431         },
46432         
46433         onSelect : function(record, index)
46434         {
46435             if(this.fireEvent('beforeselect', this, record, index) !== false){
46436                 
46437                 this.setFlagClass(record.data.iso2);
46438                 this.setDialCode(record.data.dialCode);
46439                 this.hasFocus = false;
46440                 this.collapse();
46441                 this.fireEvent('select', this, record, index);
46442             }
46443         },
46444         
46445         flagEl : function()
46446         {
46447             var flag = this.el.select('div.flag',true).first();
46448             if(!flag){
46449                 return false;
46450             }
46451             return flag;
46452         },
46453         
46454         dialCodeHolderEl : function()
46455         {
46456             var d = this.el.select('input.dial-code-holder',true).first();
46457             if(!d){
46458                 return false;
46459             }
46460             return d;
46461         },
46462         
46463         setDialCode : function(v)
46464         {
46465             this.dialCodeHolder.dom.value = '+'+v;
46466         },
46467         
46468         setFlagClass : function(n)
46469         {
46470             this.flag.dom.className = 'flag '+n;
46471         },
46472         
46473         getValue : function()
46474         {
46475             var v = this.inputEl().getValue();
46476             if(this.dialCodeHolder) {
46477                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
46478             }
46479             return v;
46480         },
46481         
46482         setValue : function(v)
46483         {
46484             var d = this.getDialCode(v);
46485             
46486             //invalid dial code
46487             if(v.length == 0 || !d || d.length == 0) {
46488                 if(this.rendered){
46489                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
46490                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
46491                 }
46492                 return;
46493             }
46494             
46495             //valid dial code
46496             this.setFlagClass(this.dialCodeMapping[d].iso2);
46497             this.setDialCode(d);
46498             this.inputEl().dom.value = v.replace('+'+d,'');
46499             this.hiddenEl().dom.value = this.getValue();
46500             
46501             this.validate();
46502         },
46503         
46504         getDialCode : function(v)
46505         {
46506             v = v ||  '';
46507             
46508             if (v.length == 0) {
46509                 return this.dialCodeHolder.dom.value;
46510             }
46511             
46512             var dialCode = "";
46513             if (v.charAt(0) != "+") {
46514                 return false;
46515             }
46516             var numericChars = "";
46517             for (var i = 1; i < v.length; i++) {
46518               var c = v.charAt(i);
46519               if (!isNaN(c)) {
46520                 numericChars += c;
46521                 if (this.dialCodeMapping[numericChars]) {
46522                   dialCode = v.substr(1, i);
46523                 }
46524                 if (numericChars.length == 4) {
46525                   break;
46526                 }
46527               }
46528             }
46529             return dialCode;
46530         },
46531         
46532         reset : function()
46533         {
46534             this.setValue(this.defaultDialCode);
46535             this.validate();
46536         },
46537         
46538         hiddenEl : function()
46539         {
46540             return this.el.select('input.hidden-tel-input',true).first();
46541         },
46542         
46543         // after setting val
46544         onKeyUp : function(e){
46545             this.setValue(this.getValue());
46546         },
46547         
46548         onKeyPress : function(e){
46549             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
46550                 e.stopEvent();
46551             }
46552         }
46553         
46554 });
46555 /**
46556  * @class Roo.bootstrap.form.MoneyField
46557  * @extends Roo.bootstrap.form.ComboBox
46558  * Bootstrap MoneyField class
46559  * 
46560  * @constructor
46561  * Create a new MoneyField.
46562  * @param {Object} config Configuration options
46563  */
46564
46565 Roo.bootstrap.form.MoneyField = function(config) {
46566     
46567     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
46568     
46569 };
46570
46571 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
46572     
46573     /**
46574      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
46575      */
46576     allowDecimals : true,
46577     /**
46578      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
46579      */
46580     decimalSeparator : ".",
46581     /**
46582      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
46583      */
46584     decimalPrecision : 0,
46585     /**
46586      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
46587      */
46588     allowNegative : true,
46589     /**
46590      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
46591      */
46592     allowZero: true,
46593     /**
46594      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
46595      */
46596     minValue : Number.NEGATIVE_INFINITY,
46597     /**
46598      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
46599      */
46600     maxValue : Number.MAX_VALUE,
46601     /**
46602      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
46603      */
46604     minText : "The minimum value for this field is {0}",
46605     /**
46606      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
46607      */
46608     maxText : "The maximum value for this field is {0}",
46609     /**
46610      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
46611      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
46612      */
46613     nanText : "{0} is not a valid number",
46614     /**
46615      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
46616      */
46617     castInt : true,
46618     /**
46619      * @cfg {String} defaults currency of the MoneyField
46620      * value should be in lkey
46621      */
46622     defaultCurrency : false,
46623     /**
46624      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
46625      */
46626     thousandsDelimiter : false,
46627     /**
46628      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
46629      */
46630     max_length: false,
46631     
46632     inputlg : 9,
46633     inputmd : 9,
46634     inputsm : 9,
46635     inputxs : 6,
46636      /**
46637      * @cfg {Roo.data.Store} store  Store to lookup currency??
46638      */
46639     store : false,
46640     
46641     getAutoCreate : function()
46642     {
46643         var align = this.labelAlign || this.parentLabelAlign();
46644         
46645         var id = Roo.id();
46646
46647         var cfg = {
46648             cls: 'form-group',
46649             cn: []
46650         };
46651
46652         var input =  {
46653             tag: 'input',
46654             id : id,
46655             cls : 'form-control roo-money-amount-input',
46656             autocomplete: 'new-password'
46657         };
46658         
46659         var hiddenInput = {
46660             tag: 'input',
46661             type: 'hidden',
46662             id: Roo.id(),
46663             cls: 'hidden-number-input'
46664         };
46665         
46666         if(this.max_length) {
46667             input.maxlength = this.max_length; 
46668         }
46669         
46670         if (this.name) {
46671             hiddenInput.name = this.name;
46672         }
46673
46674         if (this.disabled) {
46675             input.disabled = true;
46676         }
46677
46678         var clg = 12 - this.inputlg;
46679         var cmd = 12 - this.inputmd;
46680         var csm = 12 - this.inputsm;
46681         var cxs = 12 - this.inputxs;
46682         
46683         var container = {
46684             tag : 'div',
46685             cls : 'row roo-money-field',
46686             cn : [
46687                 {
46688                     tag : 'div',
46689                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
46690                     cn : [
46691                         {
46692                             tag : 'div',
46693                             cls: 'roo-select2-container input-group',
46694                             cn: [
46695                                 {
46696                                     tag : 'input',
46697                                     cls : 'form-control roo-money-currency-input',
46698                                     autocomplete: 'new-password',
46699                                     readOnly : 1,
46700                                     name : this.currencyName
46701                                 },
46702                                 {
46703                                     tag :'span',
46704                                     cls : 'input-group-addon',
46705                                     cn : [
46706                                         {
46707                                             tag: 'span',
46708                                             cls: 'caret'
46709                                         }
46710                                     ]
46711                                 }
46712                             ]
46713                         }
46714                     ]
46715                 },
46716                 {
46717                     tag : 'div',
46718                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
46719                     cn : [
46720                         {
46721                             tag: 'div',
46722                             cls: this.hasFeedback ? 'has-feedback' : '',
46723                             cn: [
46724                                 input
46725                             ]
46726                         }
46727                     ]
46728                 }
46729             ]
46730             
46731         };
46732         
46733         if (this.fieldLabel.length) {
46734             var indicator = {
46735                 tag: 'i',
46736                 tooltip: 'This field is required'
46737             };
46738
46739             var label = {
46740                 tag: 'label',
46741                 'for':  id,
46742                 cls: 'control-label',
46743                 cn: []
46744             };
46745
46746             var label_text = {
46747                 tag: 'span',
46748                 html: this.fieldLabel
46749             };
46750
46751             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46752             label.cn = [
46753                 indicator,
46754                 label_text
46755             ];
46756
46757             if(this.indicatorpos == 'right') {
46758                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46759                 label.cn = [
46760                     label_text,
46761                     indicator
46762                 ];
46763             }
46764
46765             if(align == 'left') {
46766                 container = {
46767                     tag: 'div',
46768                     cn: [
46769                         container
46770                     ]
46771                 };
46772
46773                 if(this.labelWidth > 12){
46774                     label.style = "width: " + this.labelWidth + 'px';
46775                 }
46776                 if(this.labelWidth < 13 && this.labelmd == 0){
46777                     this.labelmd = this.labelWidth;
46778                 }
46779                 if(this.labellg > 0){
46780                     label.cls += ' col-lg-' + this.labellg;
46781                     input.cls += ' col-lg-' + (12 - this.labellg);
46782                 }
46783                 if(this.labelmd > 0){
46784                     label.cls += ' col-md-' + this.labelmd;
46785                     container.cls += ' col-md-' + (12 - this.labelmd);
46786                 }
46787                 if(this.labelsm > 0){
46788                     label.cls += ' col-sm-' + this.labelsm;
46789                     container.cls += ' col-sm-' + (12 - this.labelsm);
46790                 }
46791                 if(this.labelxs > 0){
46792                     label.cls += ' col-xs-' + this.labelxs;
46793                     container.cls += ' col-xs-' + (12 - this.labelxs);
46794                 }
46795             }
46796         }
46797
46798         cfg.cn = [
46799             label,
46800             container,
46801             hiddenInput
46802         ];
46803         
46804         var settings = this;
46805
46806         ['xs','sm','md','lg'].map(function(size){
46807             if (settings[size]) {
46808                 cfg.cls += ' col-' + size + '-' + settings[size];
46809             }
46810         });
46811         
46812         return cfg;
46813     },
46814     
46815     initEvents : function()
46816     {
46817         this.indicator = this.indicatorEl();
46818         
46819         this.initCurrencyEvent();
46820         
46821         this.initNumberEvent();
46822     },
46823     
46824     initCurrencyEvent : function()
46825     {
46826         if (!this.store) {
46827             throw "can not find store for combo";
46828         }
46829         
46830         this.store = Roo.factory(this.store, Roo.data);
46831         this.store.parent = this;
46832         
46833         this.createList();
46834         
46835         this.triggerEl = this.el.select('.input-group-addon', true).first();
46836         
46837         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
46838         
46839         var _this = this;
46840         
46841         (function(){
46842             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
46843             _this.list.setWidth(lw);
46844         }).defer(100);
46845         
46846         this.list.on('mouseover', this.onViewOver, this);
46847         this.list.on('mousemove', this.onViewMove, this);
46848         this.list.on('scroll', this.onViewScroll, this);
46849         
46850         if(!this.tpl){
46851             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
46852         }
46853         
46854         this.view = new Roo.View(this.list, this.tpl, {
46855             singleSelect:true, store: this.store, selectedClass: this.selectedClass
46856         });
46857         
46858         this.view.on('click', this.onViewClick, this);
46859         
46860         this.store.on('beforeload', this.onBeforeLoad, this);
46861         this.store.on('load', this.onLoad, this);
46862         this.store.on('loadexception', this.onLoadException, this);
46863         
46864         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
46865             "up" : function(e){
46866                 this.inKeyMode = true;
46867                 this.selectPrev();
46868             },
46869
46870             "down" : function(e){
46871                 if(!this.isExpanded()){
46872                     this.onTriggerClick();
46873                 }else{
46874                     this.inKeyMode = true;
46875                     this.selectNext();
46876                 }
46877             },
46878
46879             "enter" : function(e){
46880                 this.collapse();
46881                 
46882                 if(this.fireEvent("specialkey", this, e)){
46883                     this.onViewClick(false);
46884                 }
46885                 
46886                 return true;
46887             },
46888
46889             "esc" : function(e){
46890                 this.collapse();
46891             },
46892
46893             "tab" : function(e){
46894                 this.collapse();
46895                 
46896                 if(this.fireEvent("specialkey", this, e)){
46897                     this.onViewClick(false);
46898                 }
46899                 
46900                 return true;
46901             },
46902
46903             scope : this,
46904
46905             doRelay : function(foo, bar, hname){
46906                 if(hname == 'down' || this.scope.isExpanded()){
46907                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
46908                 }
46909                 return true;
46910             },
46911
46912             forceKeyDown: true
46913         });
46914         
46915         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
46916         
46917     },
46918     
46919     initNumberEvent : function(e)
46920     {
46921         this.inputEl().on("keydown" , this.fireKey,  this);
46922         this.inputEl().on("focus", this.onFocus,  this);
46923         this.inputEl().on("blur", this.onBlur,  this);
46924         
46925         this.inputEl().relayEvent('keyup', this);
46926         
46927         if(this.indicator){
46928             this.indicator.addClass('invisible');
46929         }
46930  
46931         this.originalValue = this.getValue();
46932         
46933         if(this.validationEvent == 'keyup'){
46934             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
46935             this.inputEl().on('keyup', this.filterValidation, this);
46936         }
46937         else if(this.validationEvent !== false){
46938             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
46939         }
46940         
46941         if(this.selectOnFocus){
46942             this.on("focus", this.preFocus, this);
46943             
46944         }
46945         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
46946             this.inputEl().on("keypress", this.filterKeys, this);
46947         } else {
46948             this.inputEl().relayEvent('keypress', this);
46949         }
46950         
46951         var allowed = "0123456789";
46952         
46953         if(this.allowDecimals){
46954             allowed += this.decimalSeparator;
46955         }
46956         
46957         if(this.allowNegative){
46958             allowed += "-";
46959         }
46960         
46961         if(this.thousandsDelimiter) {
46962             allowed += ",";
46963         }
46964         
46965         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
46966         
46967         var keyPress = function(e){
46968             
46969             var k = e.getKey();
46970             
46971             var c = e.getCharCode();
46972             
46973             if(
46974                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
46975                     allowed.indexOf(String.fromCharCode(c)) === -1
46976             ){
46977                 e.stopEvent();
46978                 return;
46979             }
46980             
46981             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
46982                 return;
46983             }
46984             
46985             if(allowed.indexOf(String.fromCharCode(c)) === -1){
46986                 e.stopEvent();
46987             }
46988         };
46989         
46990         this.inputEl().on("keypress", keyPress, this);
46991         
46992     },
46993     
46994     onTriggerClick : function(e)
46995     {   
46996         if(this.disabled){
46997             return;
46998         }
46999         
47000         this.page = 0;
47001         this.loadNext = false;
47002         
47003         if(this.isExpanded()){
47004             this.collapse();
47005             return;
47006         }
47007         
47008         this.hasFocus = true;
47009         
47010         if(this.triggerAction == 'all') {
47011             this.doQuery(this.allQuery, true);
47012             return;
47013         }
47014         
47015         this.doQuery(this.getRawValue());
47016     },
47017     
47018     getCurrency : function()
47019     {   
47020         var v = this.currencyEl().getValue();
47021         
47022         return v;
47023     },
47024     
47025     restrictHeight : function()
47026     {
47027         this.list.alignTo(this.currencyEl(), this.listAlign);
47028         this.list.alignTo(this.currencyEl(), this.listAlign);
47029     },
47030     
47031     onViewClick : function(view, doFocus, el, e)
47032     {
47033         var index = this.view.getSelectedIndexes()[0];
47034         
47035         var r = this.store.getAt(index);
47036         
47037         if(r){
47038             this.onSelect(r, index);
47039         }
47040     },
47041     
47042     onSelect : function(record, index){
47043         
47044         if(this.fireEvent('beforeselect', this, record, index) !== false){
47045         
47046             this.setFromCurrencyData(index > -1 ? record.data : false);
47047             
47048             this.collapse();
47049             
47050             this.fireEvent('select', this, record, index);
47051         }
47052     },
47053     
47054     setFromCurrencyData : function(o)
47055     {
47056         var currency = '';
47057         
47058         this.lastCurrency = o;
47059         
47060         if (this.currencyField) {
47061             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
47062         } else {
47063             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
47064         }
47065         
47066         this.lastSelectionText = currency;
47067         
47068         //setting default currency
47069         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
47070             this.setCurrency(this.defaultCurrency);
47071             return;
47072         }
47073         
47074         this.setCurrency(currency);
47075     },
47076     
47077     setFromData : function(o)
47078     {
47079         var c = {};
47080         
47081         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
47082         
47083         this.setFromCurrencyData(c);
47084         
47085         var value = '';
47086         
47087         if (this.name) {
47088             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
47089         } else {
47090             Roo.log('no value set for '+ (this.name ? this.name : this.id));
47091         }
47092         
47093         this.setValue(value);
47094         
47095     },
47096     
47097     setCurrency : function(v)
47098     {   
47099         this.currencyValue = v;
47100         
47101         if(this.rendered){
47102             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
47103             this.validate();
47104         }
47105     },
47106     
47107     setValue : function(v)
47108     {
47109         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
47110         
47111         this.value = v;
47112         
47113         if(this.rendered){
47114             
47115             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
47116             
47117             this.inputEl().dom.value = (v == '') ? '' :
47118                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
47119             
47120             if(!this.allowZero && v === '0') {
47121                 this.hiddenEl().dom.value = '';
47122                 this.inputEl().dom.value = '';
47123             }
47124             
47125             this.validate();
47126         }
47127     },
47128     
47129     getRawValue : function()
47130     {
47131         var v = this.inputEl().getValue();
47132         
47133         return v;
47134     },
47135     
47136     getValue : function()
47137     {
47138         return this.fixPrecision(this.parseValue(this.getRawValue()));
47139     },
47140     
47141     parseValue : function(value)
47142     {
47143         if(this.thousandsDelimiter) {
47144             value += "";
47145             r = new RegExp(",", "g");
47146             value = value.replace(r, "");
47147         }
47148         
47149         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
47150         return isNaN(value) ? '' : value;
47151         
47152     },
47153     
47154     fixPrecision : function(value)
47155     {
47156         if(this.thousandsDelimiter) {
47157             value += "";
47158             r = new RegExp(",", "g");
47159             value = value.replace(r, "");
47160         }
47161         
47162         var nan = isNaN(value);
47163         
47164         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
47165             return nan ? '' : value;
47166         }
47167         return parseFloat(value).toFixed(this.decimalPrecision);
47168     },
47169     
47170     decimalPrecisionFcn : function(v)
47171     {
47172         return Math.floor(v);
47173     },
47174     
47175     validateValue : function(value)
47176     {
47177         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
47178             return false;
47179         }
47180         
47181         var num = this.parseValue(value);
47182         
47183         if(isNaN(num)){
47184             this.markInvalid(String.format(this.nanText, value));
47185             return false;
47186         }
47187         
47188         if(num < this.minValue){
47189             this.markInvalid(String.format(this.minText, this.minValue));
47190             return false;
47191         }
47192         
47193         if(num > this.maxValue){
47194             this.markInvalid(String.format(this.maxText, this.maxValue));
47195             return false;
47196         }
47197         
47198         return true;
47199     },
47200     
47201     validate : function()
47202     {
47203         if(this.disabled || this.allowBlank){
47204             this.markValid();
47205             return true;
47206         }
47207         
47208         var currency = this.getCurrency();
47209         
47210         if(this.validateValue(this.getRawValue()) && currency.length){
47211             this.markValid();
47212             return true;
47213         }
47214         
47215         this.markInvalid();
47216         return false;
47217     },
47218     
47219     getName: function()
47220     {
47221         return this.name;
47222     },
47223     
47224     beforeBlur : function()
47225     {
47226         if(!this.castInt){
47227             return;
47228         }
47229         
47230         var v = this.parseValue(this.getRawValue());
47231         
47232         if(v || v == 0){
47233             this.setValue(v);
47234         }
47235     },
47236     
47237     onBlur : function()
47238     {
47239         this.beforeBlur();
47240         
47241         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
47242             //this.el.removeClass(this.focusClass);
47243         }
47244         
47245         this.hasFocus = false;
47246         
47247         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
47248             this.validate();
47249         }
47250         
47251         var v = this.getValue();
47252         
47253         if(String(v) !== String(this.startValue)){
47254             this.fireEvent('change', this, v, this.startValue);
47255         }
47256         
47257         this.fireEvent("blur", this);
47258     },
47259     
47260     inputEl : function()
47261     {
47262         return this.el.select('.roo-money-amount-input', true).first();
47263     },
47264     
47265     currencyEl : function()
47266     {
47267         return this.el.select('.roo-money-currency-input', true).first();
47268     },
47269     
47270     hiddenEl : function()
47271     {
47272         return this.el.select('input.hidden-number-input',true).first();
47273     }
47274     
47275 });/**
47276  * @class Roo.bootstrap.BezierSignature
47277  * @extends Roo.bootstrap.Component
47278  * Bootstrap BezierSignature class
47279  * This script refer to:
47280  *    Title: Signature Pad
47281  *    Author: szimek
47282  *    Availability: https://github.com/szimek/signature_pad
47283  *
47284  * @constructor
47285  * Create a new BezierSignature
47286  * @param {Object} config The config object
47287  */
47288
47289 Roo.bootstrap.BezierSignature = function(config){
47290     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
47291     this.addEvents({
47292         "resize" : true
47293     });
47294 };
47295
47296 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
47297 {
47298      
47299     curve_data: [],
47300     
47301     is_empty: true,
47302     
47303     mouse_btn_down: true,
47304     
47305     /**
47306      * @cfg {int} canvas height
47307      */
47308     canvas_height: '200px',
47309     
47310     /**
47311      * @cfg {float|function} Radius of a single dot.
47312      */ 
47313     dot_size: false,
47314     
47315     /**
47316      * @cfg {float} Minimum width of a line. Defaults to 0.5.
47317      */
47318     min_width: 0.5,
47319     
47320     /**
47321      * @cfg {float} Maximum width of a line. Defaults to 2.5.
47322      */
47323     max_width: 2.5,
47324     
47325     /**
47326      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
47327      */
47328     throttle: 16,
47329     
47330     /**
47331      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
47332      */
47333     min_distance: 5,
47334     
47335     /**
47336      * @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.
47337      */
47338     bg_color: 'rgba(0, 0, 0, 0)',
47339     
47340     /**
47341      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
47342      */
47343     dot_color: 'black',
47344     
47345     /**
47346      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
47347      */ 
47348     velocity_filter_weight: 0.7,
47349     
47350     /**
47351      * @cfg {function} Callback when stroke begin. 
47352      */
47353     onBegin: false,
47354     
47355     /**
47356      * @cfg {function} Callback when stroke end.
47357      */
47358     onEnd: false,
47359     
47360     getAutoCreate : function()
47361     {
47362         var cls = 'roo-signature column';
47363         
47364         if(this.cls){
47365             cls += ' ' + this.cls;
47366         }
47367         
47368         var col_sizes = [
47369             'lg',
47370             'md',
47371             'sm',
47372             'xs'
47373         ];
47374         
47375         for(var i = 0; i < col_sizes.length; i++) {
47376             if(this[col_sizes[i]]) {
47377                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
47378             }
47379         }
47380         
47381         var cfg = {
47382             tag: 'div',
47383             cls: cls,
47384             cn: [
47385                 {
47386                     tag: 'div',
47387                     cls: 'roo-signature-body',
47388                     cn: [
47389                         {
47390                             tag: 'canvas',
47391                             cls: 'roo-signature-body-canvas',
47392                             height: this.canvas_height,
47393                             width: this.canvas_width
47394                         }
47395                     ]
47396                 },
47397                 {
47398                     tag: 'input',
47399                     type: 'file',
47400                     style: 'display: none'
47401                 }
47402             ]
47403         };
47404         
47405         return cfg;
47406     },
47407     
47408     initEvents: function() 
47409     {
47410         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
47411         
47412         var canvas = this.canvasEl();
47413         
47414         // mouse && touch event swapping...
47415         canvas.dom.style.touchAction = 'none';
47416         canvas.dom.style.msTouchAction = 'none';
47417         
47418         this.mouse_btn_down = false;
47419         canvas.on('mousedown', this._handleMouseDown, this);
47420         canvas.on('mousemove', this._handleMouseMove, this);
47421         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
47422         
47423         if (window.PointerEvent) {
47424             canvas.on('pointerdown', this._handleMouseDown, this);
47425             canvas.on('pointermove', this._handleMouseMove, this);
47426             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
47427         }
47428         
47429         if ('ontouchstart' in window) {
47430             canvas.on('touchstart', this._handleTouchStart, this);
47431             canvas.on('touchmove', this._handleTouchMove, this);
47432             canvas.on('touchend', this._handleTouchEnd, this);
47433         }
47434         
47435         Roo.EventManager.onWindowResize(this.resize, this, true);
47436         
47437         // file input event
47438         this.fileEl().on('change', this.uploadImage, this);
47439         
47440         this.clear();
47441         
47442         this.resize();
47443     },
47444     
47445     resize: function(){
47446         
47447         var canvas = this.canvasEl().dom;
47448         var ctx = this.canvasElCtx();
47449         var img_data = false;
47450         
47451         if(canvas.width > 0) {
47452             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
47453         }
47454         // setting canvas width will clean img data
47455         canvas.width = 0;
47456         
47457         var style = window.getComputedStyle ? 
47458             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
47459             
47460         var padding_left = parseInt(style.paddingLeft) || 0;
47461         var padding_right = parseInt(style.paddingRight) || 0;
47462         
47463         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
47464         
47465         if(img_data) {
47466             ctx.putImageData(img_data, 0, 0);
47467         }
47468     },
47469     
47470     _handleMouseDown: function(e)
47471     {
47472         if (e.browserEvent.which === 1) {
47473             this.mouse_btn_down = true;
47474             this.strokeBegin(e);
47475         }
47476     },
47477     
47478     _handleMouseMove: function (e)
47479     {
47480         if (this.mouse_btn_down) {
47481             this.strokeMoveUpdate(e);
47482         }
47483     },
47484     
47485     _handleMouseUp: function (e)
47486     {
47487         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
47488             this.mouse_btn_down = false;
47489             this.strokeEnd(e);
47490         }
47491     },
47492     
47493     _handleTouchStart: function (e) {
47494         
47495         e.preventDefault();
47496         if (e.browserEvent.targetTouches.length === 1) {
47497             // var touch = e.browserEvent.changedTouches[0];
47498             // this.strokeBegin(touch);
47499             
47500              this.strokeBegin(e); // assume e catching the correct xy...
47501         }
47502     },
47503     
47504     _handleTouchMove: function (e) {
47505         e.preventDefault();
47506         // var touch = event.targetTouches[0];
47507         // _this._strokeMoveUpdate(touch);
47508         this.strokeMoveUpdate(e);
47509     },
47510     
47511     _handleTouchEnd: function (e) {
47512         var wasCanvasTouched = e.target === this.canvasEl().dom;
47513         if (wasCanvasTouched) {
47514             e.preventDefault();
47515             // var touch = event.changedTouches[0];
47516             // _this._strokeEnd(touch);
47517             this.strokeEnd(e);
47518         }
47519     },
47520     
47521     reset: function () {
47522         this._lastPoints = [];
47523         this._lastVelocity = 0;
47524         this._lastWidth = (this.min_width + this.max_width) / 2;
47525         this.canvasElCtx().fillStyle = this.dot_color;
47526     },
47527     
47528     strokeMoveUpdate: function(e)
47529     {
47530         this.strokeUpdate(e);
47531         
47532         if (this.throttle) {
47533             this.throttleStroke(this.strokeUpdate, this.throttle);
47534         }
47535         else {
47536             this.strokeUpdate(e);
47537         }
47538     },
47539     
47540     strokeBegin: function(e)
47541     {
47542         var newPointGroup = {
47543             color: this.dot_color,
47544             points: []
47545         };
47546         
47547         if (typeof this.onBegin === 'function') {
47548             this.onBegin(e);
47549         }
47550         
47551         this.curve_data.push(newPointGroup);
47552         this.reset();
47553         this.strokeUpdate(e);
47554     },
47555     
47556     strokeUpdate: function(e)
47557     {
47558         var rect = this.canvasEl().dom.getBoundingClientRect();
47559         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
47560         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
47561         var lastPoints = lastPointGroup.points;
47562         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
47563         var isLastPointTooClose = lastPoint
47564             ? point.distanceTo(lastPoint) <= this.min_distance
47565             : false;
47566         var color = lastPointGroup.color;
47567         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
47568             var curve = this.addPoint(point);
47569             if (!lastPoint) {
47570                 this.drawDot({color: color, point: point});
47571             }
47572             else if (curve) {
47573                 this.drawCurve({color: color, curve: curve});
47574             }
47575             lastPoints.push({
47576                 time: point.time,
47577                 x: point.x,
47578                 y: point.y
47579             });
47580         }
47581     },
47582     
47583     strokeEnd: function(e)
47584     {
47585         this.strokeUpdate(e);
47586         if (typeof this.onEnd === 'function') {
47587             this.onEnd(e);
47588         }
47589     },
47590     
47591     addPoint:  function (point) {
47592         var _lastPoints = this._lastPoints;
47593         _lastPoints.push(point);
47594         if (_lastPoints.length > 2) {
47595             if (_lastPoints.length === 3) {
47596                 _lastPoints.unshift(_lastPoints[0]);
47597             }
47598             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
47599             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
47600             _lastPoints.shift();
47601             return curve;
47602         }
47603         return null;
47604     },
47605     
47606     calculateCurveWidths: function (startPoint, endPoint) {
47607         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
47608             (1 - this.velocity_filter_weight) * this._lastVelocity;
47609
47610         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
47611         var widths = {
47612             end: newWidth,
47613             start: this._lastWidth
47614         };
47615         
47616         this._lastVelocity = velocity;
47617         this._lastWidth = newWidth;
47618         return widths;
47619     },
47620     
47621     drawDot: function (_a) {
47622         var color = _a.color, point = _a.point;
47623         var ctx = this.canvasElCtx();
47624         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
47625         ctx.beginPath();
47626         this.drawCurveSegment(point.x, point.y, width);
47627         ctx.closePath();
47628         ctx.fillStyle = color;
47629         ctx.fill();
47630     },
47631     
47632     drawCurve: function (_a) {
47633         var color = _a.color, curve = _a.curve;
47634         var ctx = this.canvasElCtx();
47635         var widthDelta = curve.endWidth - curve.startWidth;
47636         var drawSteps = Math.floor(curve.length()) * 2;
47637         ctx.beginPath();
47638         ctx.fillStyle = color;
47639         for (var i = 0; i < drawSteps; i += 1) {
47640         var t = i / drawSteps;
47641         var tt = t * t;
47642         var ttt = tt * t;
47643         var u = 1 - t;
47644         var uu = u * u;
47645         var uuu = uu * u;
47646         var x = uuu * curve.startPoint.x;
47647         x += 3 * uu * t * curve.control1.x;
47648         x += 3 * u * tt * curve.control2.x;
47649         x += ttt * curve.endPoint.x;
47650         var y = uuu * curve.startPoint.y;
47651         y += 3 * uu * t * curve.control1.y;
47652         y += 3 * u * tt * curve.control2.y;
47653         y += ttt * curve.endPoint.y;
47654         var width = curve.startWidth + ttt * widthDelta;
47655         this.drawCurveSegment(x, y, width);
47656         }
47657         ctx.closePath();
47658         ctx.fill();
47659     },
47660     
47661     drawCurveSegment: function (x, y, width) {
47662         var ctx = this.canvasElCtx();
47663         ctx.moveTo(x, y);
47664         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
47665         this.is_empty = false;
47666     },
47667     
47668     clear: function()
47669     {
47670         var ctx = this.canvasElCtx();
47671         var canvas = this.canvasEl().dom;
47672         ctx.fillStyle = this.bg_color;
47673         ctx.clearRect(0, 0, canvas.width, canvas.height);
47674         ctx.fillRect(0, 0, canvas.width, canvas.height);
47675         this.curve_data = [];
47676         this.reset();
47677         this.is_empty = true;
47678     },
47679     
47680     fileEl: function()
47681     {
47682         return  this.el.select('input',true).first();
47683     },
47684     
47685     canvasEl: function()
47686     {
47687         return this.el.select('canvas',true).first();
47688     },
47689     
47690     canvasElCtx: function()
47691     {
47692         return this.el.select('canvas',true).first().dom.getContext('2d');
47693     },
47694     
47695     getImage: function(type)
47696     {
47697         if(this.is_empty) {
47698             return false;
47699         }
47700         
47701         // encryption ?
47702         return this.canvasEl().dom.toDataURL('image/'+type, 1);
47703     },
47704     
47705     drawFromImage: function(img_src)
47706     {
47707         var img = new Image();
47708         
47709         img.onload = function(){
47710             this.canvasElCtx().drawImage(img, 0, 0);
47711         }.bind(this);
47712         
47713         img.src = img_src;
47714         
47715         this.is_empty = false;
47716     },
47717     
47718     selectImage: function()
47719     {
47720         this.fileEl().dom.click();
47721     },
47722     
47723     uploadImage: function(e)
47724     {
47725         var reader = new FileReader();
47726         
47727         reader.onload = function(e){
47728             var img = new Image();
47729             img.onload = function(){
47730                 this.reset();
47731                 this.canvasElCtx().drawImage(img, 0, 0);
47732             }.bind(this);
47733             img.src = e.target.result;
47734         }.bind(this);
47735         
47736         reader.readAsDataURL(e.target.files[0]);
47737     },
47738     
47739     // Bezier Point Constructor
47740     Point: (function () {
47741         function Point(x, y, time) {
47742             this.x = x;
47743             this.y = y;
47744             this.time = time || Date.now();
47745         }
47746         Point.prototype.distanceTo = function (start) {
47747             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
47748         };
47749         Point.prototype.equals = function (other) {
47750             return this.x === other.x && this.y === other.y && this.time === other.time;
47751         };
47752         Point.prototype.velocityFrom = function (start) {
47753             return this.time !== start.time
47754             ? this.distanceTo(start) / (this.time - start.time)
47755             : 0;
47756         };
47757         return Point;
47758     }()),
47759     
47760     
47761     // Bezier Constructor
47762     Bezier: (function () {
47763         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
47764             this.startPoint = startPoint;
47765             this.control2 = control2;
47766             this.control1 = control1;
47767             this.endPoint = endPoint;
47768             this.startWidth = startWidth;
47769             this.endWidth = endWidth;
47770         }
47771         Bezier.fromPoints = function (points, widths, scope) {
47772             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
47773             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
47774             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
47775         };
47776         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
47777             var dx1 = s1.x - s2.x;
47778             var dy1 = s1.y - s2.y;
47779             var dx2 = s2.x - s3.x;
47780             var dy2 = s2.y - s3.y;
47781             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
47782             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
47783             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
47784             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
47785             var dxm = m1.x - m2.x;
47786             var dym = m1.y - m2.y;
47787             var k = l2 / (l1 + l2);
47788             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
47789             var tx = s2.x - cm.x;
47790             var ty = s2.y - cm.y;
47791             return {
47792                 c1: new scope.Point(m1.x + tx, m1.y + ty),
47793                 c2: new scope.Point(m2.x + tx, m2.y + ty)
47794             };
47795         };
47796         Bezier.prototype.length = function () {
47797             var steps = 10;
47798             var length = 0;
47799             var px;
47800             var py;
47801             for (var i = 0; i <= steps; i += 1) {
47802                 var t = i / steps;
47803                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
47804                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
47805                 if (i > 0) {
47806                     var xdiff = cx - px;
47807                     var ydiff = cy - py;
47808                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
47809                 }
47810                 px = cx;
47811                 py = cy;
47812             }
47813             return length;
47814         };
47815         Bezier.prototype.point = function (t, start, c1, c2, end) {
47816             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
47817             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
47818             + (3.0 * c2 * (1.0 - t) * t * t)
47819             + (end * t * t * t);
47820         };
47821         return Bezier;
47822     }()),
47823     
47824     throttleStroke: function(fn, wait) {
47825       if (wait === void 0) { wait = 250; }
47826       var previous = 0;
47827       var timeout = null;
47828       var result;
47829       var storedContext;
47830       var storedArgs;
47831       var later = function () {
47832           previous = Date.now();
47833           timeout = null;
47834           result = fn.apply(storedContext, storedArgs);
47835           if (!timeout) {
47836               storedContext = null;
47837               storedArgs = [];
47838           }
47839       };
47840       return function wrapper() {
47841           var args = [];
47842           for (var _i = 0; _i < arguments.length; _i++) {
47843               args[_i] = arguments[_i];
47844           }
47845           var now = Date.now();
47846           var remaining = wait - (now - previous);
47847           storedContext = this;
47848           storedArgs = args;
47849           if (remaining <= 0 || remaining > wait) {
47850               if (timeout) {
47851                   clearTimeout(timeout);
47852                   timeout = null;
47853               }
47854               previous = now;
47855               result = fn.apply(storedContext, storedArgs);
47856               if (!timeout) {
47857                   storedContext = null;
47858                   storedArgs = [];
47859               }
47860           }
47861           else if (!timeout) {
47862               timeout = window.setTimeout(later, remaining);
47863           }
47864           return result;
47865       };
47866   }
47867   
47868 });
47869
47870  
47871
47872  // old names for form elements
47873 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
47874 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
47875 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
47876 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
47877 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
47878 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
47879 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
47880 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
47881 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
47882 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
47883 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
47884 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
47885 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
47886 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
47887 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
47888 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
47889 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
47890 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
47891 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
47892 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
47893 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
47894 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
47895 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
47896 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
47897 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
47898 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
47899
47900 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
47901 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
47902
47903 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
47904 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
47905
47906 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
47907 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
47908 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
47909 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
47910