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         
33522         if (this.buttons.length) {
33523             
33524             Roo.each(this.buttons, function(bb) {
33525                 
33526                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
33527                 
33528                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
33529                 
33530             }, this);
33531         }
33532         
33533         if(this.loadMask){
33534             this.maskEl = this.el;
33535         }
33536     },
33537     
33538     initEvents : function()
33539     {
33540         this.urlAPI = (window.createObjectURL && window) || 
33541                                 (window.URL && URL.revokeObjectURL && URL) || 
33542                                 (window.webkitURL && webkitURL);
33543                         
33544         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
33545         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33546         
33547         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
33548         this.selectorEl.hide();
33549         
33550         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
33551         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33552         
33553         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
33554         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33555         this.thumbEl.hide();
33556         
33557         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
33558         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33559         
33560         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
33561         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33562         this.errorEl.hide();
33563         
33564         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
33565         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33566         this.footerEl.hide();
33567         
33568         this.setThumbBoxSize();
33569         
33570         this.bind();
33571         
33572         this.resize();
33573         
33574         this.fireEvent('initial', this);
33575     },
33576
33577     bind : function()
33578     {
33579         var _this = this;
33580         
33581         window.addEventListener("resize", function() { _this.resize(); } );
33582         
33583         this.bodyEl.on('click', this.beforeSelectFile, this);
33584         
33585         if(Roo.isTouch){
33586             this.bodyEl.on('touchstart', this.onTouchStart, this);
33587             this.bodyEl.on('touchmove', this.onTouchMove, this);
33588             this.bodyEl.on('touchend', this.onTouchEnd, this);
33589         }
33590         
33591         if(!Roo.isTouch){
33592             this.bodyEl.on('mousedown', this.onMouseDown, this);
33593             this.bodyEl.on('mousemove', this.onMouseMove, this);
33594             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
33595             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
33596             Roo.get(document).on('mouseup', this.onMouseUp, this);
33597         }
33598         
33599         this.selectorEl.on('change', this.onFileSelected, this);
33600     },
33601     
33602     reset : function()
33603     {    
33604         this.scale = 0;
33605         this.baseScale = 1;
33606         this.rotate = 0;
33607         this.baseRotate = 1;
33608         this.dragable = false;
33609         this.pinching = false;
33610         this.mouseX = 0;
33611         this.mouseY = 0;
33612         this.cropData = false;
33613         this.notifyEl.dom.innerHTML = this.emptyText;
33614         
33615         this.selectorEl.dom.value = '';
33616         
33617     },
33618     
33619     resize : function()
33620     {
33621         if(this.fireEvent('resize', this) != false){
33622             this.setThumbBoxPosition();
33623             this.setCanvasPosition();
33624         }
33625     },
33626     
33627     onFooterButtonClick : function(e, el, o, type)
33628     {
33629         switch (type) {
33630             case 'rotate-left' :
33631                 this.onRotateLeft(e);
33632                 break;
33633             case 'rotate-right' :
33634                 this.onRotateRight(e);
33635                 break;
33636             case 'picture' :
33637                 this.beforeSelectFile(e);
33638                 break;
33639             case 'trash' :
33640                 this.trash(e);
33641                 break;
33642             case 'crop' :
33643                 this.crop(e);
33644                 break;
33645             case 'download' :
33646                 this.download(e);
33647                 break;
33648             default :
33649                 break;
33650         }
33651         
33652         this.fireEvent('footerbuttonclick', this, type);
33653     },
33654     
33655     beforeSelectFile : function(e)
33656     {
33657         e.preventDefault();
33658         
33659         if(this.fireEvent('beforeselectfile', this) != false){
33660             this.selectorEl.dom.click();
33661         }
33662     },
33663     
33664     onFileSelected : function(e)
33665     {
33666         e.preventDefault();
33667         
33668         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
33669             return;
33670         }
33671         
33672         var file = this.selectorEl.dom.files[0];
33673         
33674         if(this.fireEvent('inspect', this, file) != false){
33675             this.prepare(file);
33676         }
33677         
33678     },
33679     
33680     trash : function(e)
33681     {
33682         this.fireEvent('trash', this);
33683     },
33684     
33685     download : function(e)
33686     {
33687         this.fireEvent('download', this);
33688     },
33689     
33690     loadCanvas : function(src)
33691     {   
33692         if(this.fireEvent('beforeloadcanvas', this, src) != false){
33693             
33694             this.reset();
33695             
33696             this.imageEl = document.createElement('img');
33697             
33698             var _this = this;
33699             
33700             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
33701             
33702             this.imageEl.src = src;
33703         }
33704     },
33705     
33706     onLoadCanvas : function()
33707     {   
33708         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
33709         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
33710         
33711         this.bodyEl.un('click', this.beforeSelectFile, this);
33712         
33713         this.notifyEl.hide();
33714         this.thumbEl.show();
33715         this.footerEl.show();
33716         
33717         this.baseRotateLevel();
33718         
33719         if(this.isDocument){
33720             this.setThumbBoxSize();
33721         }
33722         
33723         this.setThumbBoxPosition();
33724         
33725         this.baseScaleLevel();
33726         
33727         this.draw();
33728         
33729         this.resize();
33730         
33731         this.canvasLoaded = true;
33732         
33733         if(this.loadMask){
33734             this.maskEl.unmask();
33735         }
33736         
33737     },
33738     
33739     setCanvasPosition : function()
33740     {   
33741         if(!this.canvasEl){
33742             return;
33743         }
33744         
33745         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
33746         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
33747         
33748         this.previewEl.setLeft(pw);
33749         this.previewEl.setTop(ph);
33750         
33751     },
33752     
33753     onMouseDown : function(e)
33754     {   
33755         e.stopEvent();
33756         
33757         this.dragable = true;
33758         this.pinching = false;
33759         
33760         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
33761             this.dragable = false;
33762             return;
33763         }
33764         
33765         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33766         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33767         
33768     },
33769     
33770     onMouseMove : function(e)
33771     {   
33772         e.stopEvent();
33773         
33774         if(!this.canvasLoaded){
33775             return;
33776         }
33777         
33778         if (!this.dragable){
33779             return;
33780         }
33781         
33782         var minX = Math.ceil(this.thumbEl.getLeft(true));
33783         var minY = Math.ceil(this.thumbEl.getTop(true));
33784         
33785         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
33786         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
33787         
33788         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33789         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33790         
33791         x = x - this.mouseX;
33792         y = y - this.mouseY;
33793         
33794         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
33795         var bgY = Math.ceil(y + this.previewEl.getTop(true));
33796         
33797         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
33798         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
33799         
33800         this.previewEl.setLeft(bgX);
33801         this.previewEl.setTop(bgY);
33802         
33803         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33804         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33805     },
33806     
33807     onMouseUp : function(e)
33808     {   
33809         e.stopEvent();
33810         
33811         this.dragable = false;
33812     },
33813     
33814     onMouseWheel : function(e)
33815     {   
33816         e.stopEvent();
33817         
33818         this.startScale = this.scale;
33819         
33820         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
33821         
33822         if(!this.zoomable()){
33823             this.scale = this.startScale;
33824             return;
33825         }
33826         
33827         this.draw();
33828         
33829         return;
33830     },
33831     
33832     zoomable : function()
33833     {
33834         var minScale = this.thumbEl.getWidth() / this.minWidth;
33835         
33836         if(this.minWidth < this.minHeight){
33837             minScale = this.thumbEl.getHeight() / this.minHeight;
33838         }
33839         
33840         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
33841         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
33842         
33843         if(
33844                 this.isDocument &&
33845                 (this.rotate == 0 || this.rotate == 180) && 
33846                 (
33847                     width > this.imageEl.OriginWidth || 
33848                     height > this.imageEl.OriginHeight ||
33849                     (width < this.minWidth && height < this.minHeight)
33850                 )
33851         ){
33852             return false;
33853         }
33854         
33855         if(
33856                 this.isDocument &&
33857                 (this.rotate == 90 || this.rotate == 270) && 
33858                 (
33859                     width > this.imageEl.OriginWidth || 
33860                     height > this.imageEl.OriginHeight ||
33861                     (width < this.minHeight && height < this.minWidth)
33862                 )
33863         ){
33864             return false;
33865         }
33866         
33867         if(
33868                 !this.isDocument &&
33869                 (this.rotate == 0 || this.rotate == 180) && 
33870                 (
33871                     width < this.minWidth || 
33872                     width > this.imageEl.OriginWidth || 
33873                     height < this.minHeight || 
33874                     height > this.imageEl.OriginHeight
33875                 )
33876         ){
33877             return false;
33878         }
33879         
33880         if(
33881                 !this.isDocument &&
33882                 (this.rotate == 90 || this.rotate == 270) && 
33883                 (
33884                     width < this.minHeight || 
33885                     width > this.imageEl.OriginWidth || 
33886                     height < this.minWidth || 
33887                     height > this.imageEl.OriginHeight
33888                 )
33889         ){
33890             return false;
33891         }
33892         
33893         return true;
33894         
33895     },
33896     
33897     onRotateLeft : function(e)
33898     {   
33899         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
33900             
33901             var minScale = this.thumbEl.getWidth() / this.minWidth;
33902             
33903             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
33904             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
33905             
33906             this.startScale = this.scale;
33907             
33908             while (this.getScaleLevel() < minScale){
33909             
33910                 this.scale = this.scale + 1;
33911                 
33912                 if(!this.zoomable()){
33913                     break;
33914                 }
33915                 
33916                 if(
33917                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
33918                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
33919                 ){
33920                     continue;
33921                 }
33922                 
33923                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
33924
33925                 this.draw();
33926                 
33927                 return;
33928             }
33929             
33930             this.scale = this.startScale;
33931             
33932             this.onRotateFail();
33933             
33934             return false;
33935         }
33936         
33937         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
33938
33939         if(this.isDocument){
33940             this.setThumbBoxSize();
33941             this.setThumbBoxPosition();
33942             this.setCanvasPosition();
33943         }
33944         
33945         this.draw();
33946         
33947         this.fireEvent('rotate', this, 'left');
33948         
33949     },
33950     
33951     onRotateRight : function(e)
33952     {
33953         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
33954             
33955             var minScale = this.thumbEl.getWidth() / this.minWidth;
33956         
33957             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
33958             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
33959             
33960             this.startScale = this.scale;
33961             
33962             while (this.getScaleLevel() < minScale){
33963             
33964                 this.scale = this.scale + 1;
33965                 
33966                 if(!this.zoomable()){
33967                     break;
33968                 }
33969                 
33970                 if(
33971                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
33972                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
33973                 ){
33974                     continue;
33975                 }
33976                 
33977                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
33978
33979                 this.draw();
33980                 
33981                 return;
33982             }
33983             
33984             this.scale = this.startScale;
33985             
33986             this.onRotateFail();
33987             
33988             return false;
33989         }
33990         
33991         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
33992
33993         if(this.isDocument){
33994             this.setThumbBoxSize();
33995             this.setThumbBoxPosition();
33996             this.setCanvasPosition();
33997         }
33998         
33999         this.draw();
34000         
34001         this.fireEvent('rotate', this, 'right');
34002     },
34003     
34004     onRotateFail : function()
34005     {
34006         this.errorEl.show(true);
34007         
34008         var _this = this;
34009         
34010         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
34011     },
34012     
34013     draw : function()
34014     {
34015         this.previewEl.dom.innerHTML = '';
34016         
34017         var canvasEl = document.createElement("canvas");
34018         
34019         var contextEl = canvasEl.getContext("2d");
34020         
34021         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34022         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34023         var center = this.imageEl.OriginWidth / 2;
34024         
34025         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
34026             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34027             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34028             center = this.imageEl.OriginHeight / 2;
34029         }
34030         
34031         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
34032         
34033         contextEl.translate(center, center);
34034         contextEl.rotate(this.rotate * Math.PI / 180);
34035
34036         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34037         
34038         this.canvasEl = document.createElement("canvas");
34039         
34040         this.contextEl = this.canvasEl.getContext("2d");
34041         
34042         switch (this.rotate) {
34043             case 0 :
34044                 
34045                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34046                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34047                 
34048                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34049                 
34050                 break;
34051             case 90 : 
34052                 
34053                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34054                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34055                 
34056                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34057                     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);
34058                     break;
34059                 }
34060                 
34061                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34062                 
34063                 break;
34064             case 180 :
34065                 
34066                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34067                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34068                 
34069                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34070                     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);
34071                     break;
34072                 }
34073                 
34074                 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);
34075                 
34076                 break;
34077             case 270 :
34078                 
34079                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34080                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34081         
34082                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34083                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34084                     break;
34085                 }
34086                 
34087                 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);
34088                 
34089                 break;
34090             default : 
34091                 break;
34092         }
34093         
34094         this.previewEl.appendChild(this.canvasEl);
34095         
34096         this.setCanvasPosition();
34097     },
34098     
34099     crop : function()
34100     {
34101         if(!this.canvasLoaded){
34102             return;
34103         }
34104         
34105         var imageCanvas = document.createElement("canvas");
34106         
34107         var imageContext = imageCanvas.getContext("2d");
34108         
34109         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34110         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34111         
34112         var center = imageCanvas.width / 2;
34113         
34114         imageContext.translate(center, center);
34115         
34116         imageContext.rotate(this.rotate * Math.PI / 180);
34117         
34118         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34119         
34120         var canvas = document.createElement("canvas");
34121         
34122         var context = canvas.getContext("2d");
34123                 
34124         canvas.width = this.minWidth;
34125         canvas.height = this.minHeight;
34126
34127         switch (this.rotate) {
34128             case 0 :
34129                 
34130                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34131                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34132                 
34133                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34134                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34135                 
34136                 var targetWidth = this.minWidth - 2 * x;
34137                 var targetHeight = this.minHeight - 2 * y;
34138                 
34139                 var scale = 1;
34140                 
34141                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34142                     scale = targetWidth / width;
34143                 }
34144                 
34145                 if(x > 0 && y == 0){
34146                     scale = targetHeight / height;
34147                 }
34148                 
34149                 if(x > 0 && y > 0){
34150                     scale = targetWidth / width;
34151                     
34152                     if(width < height){
34153                         scale = targetHeight / height;
34154                     }
34155                 }
34156                 
34157                 context.scale(scale, scale);
34158                 
34159                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34160                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34161
34162                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34163                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34164
34165                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34166                 
34167                 break;
34168             case 90 : 
34169                 
34170                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34171                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34172                 
34173                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34174                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34175                 
34176                 var targetWidth = this.minWidth - 2 * x;
34177                 var targetHeight = this.minHeight - 2 * y;
34178                 
34179                 var scale = 1;
34180                 
34181                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34182                     scale = targetWidth / width;
34183                 }
34184                 
34185                 if(x > 0 && y == 0){
34186                     scale = targetHeight / height;
34187                 }
34188                 
34189                 if(x > 0 && y > 0){
34190                     scale = targetWidth / width;
34191                     
34192                     if(width < height){
34193                         scale = targetHeight / height;
34194                     }
34195                 }
34196                 
34197                 context.scale(scale, scale);
34198                 
34199                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34200                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34201
34202                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34203                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34204                 
34205                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34206                 
34207                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34208                 
34209                 break;
34210             case 180 :
34211                 
34212                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34213                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34214                 
34215                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34216                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34217                 
34218                 var targetWidth = this.minWidth - 2 * x;
34219                 var targetHeight = this.minHeight - 2 * y;
34220                 
34221                 var scale = 1;
34222                 
34223                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34224                     scale = targetWidth / width;
34225                 }
34226                 
34227                 if(x > 0 && y == 0){
34228                     scale = targetHeight / height;
34229                 }
34230                 
34231                 if(x > 0 && y > 0){
34232                     scale = targetWidth / width;
34233                     
34234                     if(width < height){
34235                         scale = targetHeight / height;
34236                     }
34237                 }
34238                 
34239                 context.scale(scale, scale);
34240                 
34241                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34242                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34243
34244                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34245                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34246
34247                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34248                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34249                 
34250                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34251                 
34252                 break;
34253             case 270 :
34254                 
34255                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34256                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34257                 
34258                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34259                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34260                 
34261                 var targetWidth = this.minWidth - 2 * x;
34262                 var targetHeight = this.minHeight - 2 * y;
34263                 
34264                 var scale = 1;
34265                 
34266                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34267                     scale = targetWidth / width;
34268                 }
34269                 
34270                 if(x > 0 && y == 0){
34271                     scale = targetHeight / height;
34272                 }
34273                 
34274                 if(x > 0 && y > 0){
34275                     scale = targetWidth / width;
34276                     
34277                     if(width < height){
34278                         scale = targetHeight / height;
34279                     }
34280                 }
34281                 
34282                 context.scale(scale, scale);
34283                 
34284                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34285                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34286
34287                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34288                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34289                 
34290                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34291                 
34292                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34293                 
34294                 break;
34295             default : 
34296                 break;
34297         }
34298         
34299         this.cropData = canvas.toDataURL(this.cropType);
34300         
34301         if(this.fireEvent('crop', this, this.cropData) !== false){
34302             this.process(this.file, this.cropData);
34303         }
34304         
34305         return;
34306         
34307     },
34308     
34309     setThumbBoxSize : function()
34310     {
34311         var width, height;
34312         
34313         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
34314             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
34315             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
34316             
34317             this.minWidth = width;
34318             this.minHeight = height;
34319             
34320             if(this.rotate == 90 || this.rotate == 270){
34321                 this.minWidth = height;
34322                 this.minHeight = width;
34323             }
34324         }
34325         
34326         height = 300;
34327         width = Math.ceil(this.minWidth * height / this.minHeight);
34328         
34329         if(this.minWidth > this.minHeight){
34330             width = 300;
34331             height = Math.ceil(this.minHeight * width / this.minWidth);
34332         }
34333         
34334         this.thumbEl.setStyle({
34335             width : width + 'px',
34336             height : height + 'px'
34337         });
34338
34339         return;
34340             
34341     },
34342     
34343     setThumbBoxPosition : function()
34344     {
34345         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
34346         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
34347         
34348         this.thumbEl.setLeft(x);
34349         this.thumbEl.setTop(y);
34350         
34351     },
34352     
34353     baseRotateLevel : function()
34354     {
34355         this.baseRotate = 1;
34356         
34357         if(
34358                 typeof(this.exif) != 'undefined' &&
34359                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
34360                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
34361         ){
34362             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
34363         }
34364         
34365         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
34366         
34367     },
34368     
34369     baseScaleLevel : function()
34370     {
34371         var width, height;
34372         
34373         if(this.isDocument){
34374             
34375             if(this.baseRotate == 6 || this.baseRotate == 8){
34376             
34377                 height = this.thumbEl.getHeight();
34378                 this.baseScale = height / this.imageEl.OriginWidth;
34379
34380                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
34381                     width = this.thumbEl.getWidth();
34382                     this.baseScale = width / this.imageEl.OriginHeight;
34383                 }
34384
34385                 return;
34386             }
34387
34388             height = this.thumbEl.getHeight();
34389             this.baseScale = height / this.imageEl.OriginHeight;
34390
34391             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
34392                 width = this.thumbEl.getWidth();
34393                 this.baseScale = width / this.imageEl.OriginWidth;
34394             }
34395
34396             return;
34397         }
34398         
34399         if(this.baseRotate == 6 || this.baseRotate == 8){
34400             
34401             width = this.thumbEl.getHeight();
34402             this.baseScale = width / this.imageEl.OriginHeight;
34403             
34404             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
34405                 height = this.thumbEl.getWidth();
34406                 this.baseScale = height / this.imageEl.OriginHeight;
34407             }
34408             
34409             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34410                 height = this.thumbEl.getWidth();
34411                 this.baseScale = height / this.imageEl.OriginHeight;
34412                 
34413                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
34414                     width = this.thumbEl.getHeight();
34415                     this.baseScale = width / this.imageEl.OriginWidth;
34416                 }
34417             }
34418             
34419             return;
34420         }
34421         
34422         width = this.thumbEl.getWidth();
34423         this.baseScale = width / this.imageEl.OriginWidth;
34424         
34425         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
34426             height = this.thumbEl.getHeight();
34427             this.baseScale = height / this.imageEl.OriginHeight;
34428         }
34429         
34430         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34431             
34432             height = this.thumbEl.getHeight();
34433             this.baseScale = height / this.imageEl.OriginHeight;
34434             
34435             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
34436                 width = this.thumbEl.getWidth();
34437                 this.baseScale = width / this.imageEl.OriginWidth;
34438             }
34439             
34440         }
34441         
34442         return;
34443     },
34444     
34445     getScaleLevel : function()
34446     {
34447         return this.baseScale * Math.pow(1.1, this.scale);
34448     },
34449     
34450     onTouchStart : function(e)
34451     {
34452         if(!this.canvasLoaded){
34453             this.beforeSelectFile(e);
34454             return;
34455         }
34456         
34457         var touches = e.browserEvent.touches;
34458         
34459         if(!touches){
34460             return;
34461         }
34462         
34463         if(touches.length == 1){
34464             this.onMouseDown(e);
34465             return;
34466         }
34467         
34468         if(touches.length != 2){
34469             return;
34470         }
34471         
34472         var coords = [];
34473         
34474         for(var i = 0, finger; finger = touches[i]; i++){
34475             coords.push(finger.pageX, finger.pageY);
34476         }
34477         
34478         var x = Math.pow(coords[0] - coords[2], 2);
34479         var y = Math.pow(coords[1] - coords[3], 2);
34480         
34481         this.startDistance = Math.sqrt(x + y);
34482         
34483         this.startScale = this.scale;
34484         
34485         this.pinching = true;
34486         this.dragable = false;
34487         
34488     },
34489     
34490     onTouchMove : function(e)
34491     {
34492         if(!this.pinching && !this.dragable){
34493             return;
34494         }
34495         
34496         var touches = e.browserEvent.touches;
34497         
34498         if(!touches){
34499             return;
34500         }
34501         
34502         if(this.dragable){
34503             this.onMouseMove(e);
34504             return;
34505         }
34506         
34507         var coords = [];
34508         
34509         for(var i = 0, finger; finger = touches[i]; i++){
34510             coords.push(finger.pageX, finger.pageY);
34511         }
34512         
34513         var x = Math.pow(coords[0] - coords[2], 2);
34514         var y = Math.pow(coords[1] - coords[3], 2);
34515         
34516         this.endDistance = Math.sqrt(x + y);
34517         
34518         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
34519         
34520         if(!this.zoomable()){
34521             this.scale = this.startScale;
34522             return;
34523         }
34524         
34525         this.draw();
34526         
34527     },
34528     
34529     onTouchEnd : function(e)
34530     {
34531         this.pinching = false;
34532         this.dragable = false;
34533         
34534     },
34535     
34536     process : function(file, crop)
34537     {
34538         if(this.loadMask){
34539             this.maskEl.mask(this.loadingText);
34540         }
34541         
34542         this.xhr = new XMLHttpRequest();
34543         
34544         file.xhr = this.xhr;
34545
34546         this.xhr.open(this.method, this.url, true);
34547         
34548         var headers = {
34549             "Accept": "application/json",
34550             "Cache-Control": "no-cache",
34551             "X-Requested-With": "XMLHttpRequest"
34552         };
34553         
34554         for (var headerName in headers) {
34555             var headerValue = headers[headerName];
34556             if (headerValue) {
34557                 this.xhr.setRequestHeader(headerName, headerValue);
34558             }
34559         }
34560         
34561         var _this = this;
34562         
34563         this.xhr.onload = function()
34564         {
34565             _this.xhrOnLoad(_this.xhr);
34566         }
34567         
34568         this.xhr.onerror = function()
34569         {
34570             _this.xhrOnError(_this.xhr);
34571         }
34572         
34573         var formData = new FormData();
34574
34575         formData.append('returnHTML', 'NO');
34576         
34577         if(crop){
34578             formData.append('crop', crop);
34579         }
34580         
34581         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
34582             formData.append(this.paramName, file, file.name);
34583         }
34584         
34585         if(typeof(file.filename) != 'undefined'){
34586             formData.append('filename', file.filename);
34587         }
34588         
34589         if(typeof(file.mimetype) != 'undefined'){
34590             formData.append('mimetype', file.mimetype);
34591         }
34592         
34593         if(this.fireEvent('arrange', this, formData) != false){
34594             this.xhr.send(formData);
34595         };
34596     },
34597     
34598     xhrOnLoad : function(xhr)
34599     {
34600         if(this.loadMask){
34601             this.maskEl.unmask();
34602         }
34603         
34604         if (xhr.readyState !== 4) {
34605             this.fireEvent('exception', this, xhr);
34606             return;
34607         }
34608
34609         var response = Roo.decode(xhr.responseText);
34610         
34611         if(!response.success){
34612             this.fireEvent('exception', this, xhr);
34613             return;
34614         }
34615         
34616         var response = Roo.decode(xhr.responseText);
34617         
34618         this.fireEvent('upload', this, response);
34619         
34620     },
34621     
34622     xhrOnError : function()
34623     {
34624         if(this.loadMask){
34625             this.maskEl.unmask();
34626         }
34627         
34628         Roo.log('xhr on error');
34629         
34630         var response = Roo.decode(xhr.responseText);
34631           
34632         Roo.log(response);
34633         
34634     },
34635     
34636     prepare : function(file)
34637     {   
34638         if(this.loadMask){
34639             this.maskEl.mask(this.loadingText);
34640         }
34641         
34642         this.file = false;
34643         this.exif = {};
34644         
34645         if(typeof(file) === 'string'){
34646             this.loadCanvas(file);
34647             return;
34648         }
34649         
34650         if(!file || !this.urlAPI){
34651             return;
34652         }
34653         
34654         this.file = file;
34655         this.cropType = file.type;
34656         
34657         var _this = this;
34658         
34659         if(this.fireEvent('prepare', this, this.file) != false){
34660             
34661             var reader = new FileReader();
34662             
34663             reader.onload = function (e) {
34664                 if (e.target.error) {
34665                     Roo.log(e.target.error);
34666                     return;
34667                 }
34668                 
34669                 var buffer = e.target.result,
34670                     dataView = new DataView(buffer),
34671                     offset = 2,
34672                     maxOffset = dataView.byteLength - 4,
34673                     markerBytes,
34674                     markerLength;
34675                 
34676                 if (dataView.getUint16(0) === 0xffd8) {
34677                     while (offset < maxOffset) {
34678                         markerBytes = dataView.getUint16(offset);
34679                         
34680                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
34681                             markerLength = dataView.getUint16(offset + 2) + 2;
34682                             if (offset + markerLength > dataView.byteLength) {
34683                                 Roo.log('Invalid meta data: Invalid segment size.');
34684                                 break;
34685                             }
34686                             
34687                             if(markerBytes == 0xffe1){
34688                                 _this.parseExifData(
34689                                     dataView,
34690                                     offset,
34691                                     markerLength
34692                                 );
34693                             }
34694                             
34695                             offset += markerLength;
34696                             
34697                             continue;
34698                         }
34699                         
34700                         break;
34701                     }
34702                     
34703                 }
34704                 
34705                 var url = _this.urlAPI.createObjectURL(_this.file);
34706                 
34707                 _this.loadCanvas(url);
34708                 
34709                 return;
34710             }
34711             
34712             reader.readAsArrayBuffer(this.file);
34713             
34714         }
34715         
34716     },
34717     
34718     parseExifData : function(dataView, offset, length)
34719     {
34720         var tiffOffset = offset + 10,
34721             littleEndian,
34722             dirOffset;
34723     
34724         if (dataView.getUint32(offset + 4) !== 0x45786966) {
34725             // No Exif data, might be XMP data instead
34726             return;
34727         }
34728         
34729         // Check for the ASCII code for "Exif" (0x45786966):
34730         if (dataView.getUint32(offset + 4) !== 0x45786966) {
34731             // No Exif data, might be XMP data instead
34732             return;
34733         }
34734         if (tiffOffset + 8 > dataView.byteLength) {
34735             Roo.log('Invalid Exif data: Invalid segment size.');
34736             return;
34737         }
34738         // Check for the two null bytes:
34739         if (dataView.getUint16(offset + 8) !== 0x0000) {
34740             Roo.log('Invalid Exif data: Missing byte alignment offset.');
34741             return;
34742         }
34743         // Check the byte alignment:
34744         switch (dataView.getUint16(tiffOffset)) {
34745         case 0x4949:
34746             littleEndian = true;
34747             break;
34748         case 0x4D4D:
34749             littleEndian = false;
34750             break;
34751         default:
34752             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
34753             return;
34754         }
34755         // Check for the TIFF tag marker (0x002A):
34756         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
34757             Roo.log('Invalid Exif data: Missing TIFF marker.');
34758             return;
34759         }
34760         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
34761         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
34762         
34763         this.parseExifTags(
34764             dataView,
34765             tiffOffset,
34766             tiffOffset + dirOffset,
34767             littleEndian
34768         );
34769     },
34770     
34771     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
34772     {
34773         var tagsNumber,
34774             dirEndOffset,
34775             i;
34776         if (dirOffset + 6 > dataView.byteLength) {
34777             Roo.log('Invalid Exif data: Invalid directory offset.');
34778             return;
34779         }
34780         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
34781         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
34782         if (dirEndOffset + 4 > dataView.byteLength) {
34783             Roo.log('Invalid Exif data: Invalid directory size.');
34784             return;
34785         }
34786         for (i = 0; i < tagsNumber; i += 1) {
34787             this.parseExifTag(
34788                 dataView,
34789                 tiffOffset,
34790                 dirOffset + 2 + 12 * i, // tag offset
34791                 littleEndian
34792             );
34793         }
34794         // Return the offset to the next directory:
34795         return dataView.getUint32(dirEndOffset, littleEndian);
34796     },
34797     
34798     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
34799     {
34800         var tag = dataView.getUint16(offset, littleEndian);
34801         
34802         this.exif[tag] = this.getExifValue(
34803             dataView,
34804             tiffOffset,
34805             offset,
34806             dataView.getUint16(offset + 2, littleEndian), // tag type
34807             dataView.getUint32(offset + 4, littleEndian), // tag length
34808             littleEndian
34809         );
34810     },
34811     
34812     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
34813     {
34814         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
34815             tagSize,
34816             dataOffset,
34817             values,
34818             i,
34819             str,
34820             c;
34821     
34822         if (!tagType) {
34823             Roo.log('Invalid Exif data: Invalid tag type.');
34824             return;
34825         }
34826         
34827         tagSize = tagType.size * length;
34828         // Determine if the value is contained in the dataOffset bytes,
34829         // or if the value at the dataOffset is a pointer to the actual data:
34830         dataOffset = tagSize > 4 ?
34831                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
34832         if (dataOffset + tagSize > dataView.byteLength) {
34833             Roo.log('Invalid Exif data: Invalid data offset.');
34834             return;
34835         }
34836         if (length === 1) {
34837             return tagType.getValue(dataView, dataOffset, littleEndian);
34838         }
34839         values = [];
34840         for (i = 0; i < length; i += 1) {
34841             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
34842         }
34843         
34844         if (tagType.ascii) {
34845             str = '';
34846             // Concatenate the chars:
34847             for (i = 0; i < values.length; i += 1) {
34848                 c = values[i];
34849                 // Ignore the terminating NULL byte(s):
34850                 if (c === '\u0000') {
34851                     break;
34852                 }
34853                 str += c;
34854             }
34855             return str;
34856         }
34857         return values;
34858     }
34859     
34860 });
34861
34862 Roo.apply(Roo.bootstrap.UploadCropbox, {
34863     tags : {
34864         'Orientation': 0x0112
34865     },
34866     
34867     Orientation: {
34868             1: 0, //'top-left',
34869 //            2: 'top-right',
34870             3: 180, //'bottom-right',
34871 //            4: 'bottom-left',
34872 //            5: 'left-top',
34873             6: 90, //'right-top',
34874 //            7: 'right-bottom',
34875             8: 270 //'left-bottom'
34876     },
34877     
34878     exifTagTypes : {
34879         // byte, 8-bit unsigned int:
34880         1: {
34881             getValue: function (dataView, dataOffset) {
34882                 return dataView.getUint8(dataOffset);
34883             },
34884             size: 1
34885         },
34886         // ascii, 8-bit byte:
34887         2: {
34888             getValue: function (dataView, dataOffset) {
34889                 return String.fromCharCode(dataView.getUint8(dataOffset));
34890             },
34891             size: 1,
34892             ascii: true
34893         },
34894         // short, 16 bit int:
34895         3: {
34896             getValue: function (dataView, dataOffset, littleEndian) {
34897                 return dataView.getUint16(dataOffset, littleEndian);
34898             },
34899             size: 2
34900         },
34901         // long, 32 bit int:
34902         4: {
34903             getValue: function (dataView, dataOffset, littleEndian) {
34904                 return dataView.getUint32(dataOffset, littleEndian);
34905             },
34906             size: 4
34907         },
34908         // rational = two long values, first is numerator, second is denominator:
34909         5: {
34910             getValue: function (dataView, dataOffset, littleEndian) {
34911                 return dataView.getUint32(dataOffset, littleEndian) /
34912                     dataView.getUint32(dataOffset + 4, littleEndian);
34913             },
34914             size: 8
34915         },
34916         // slong, 32 bit signed int:
34917         9: {
34918             getValue: function (dataView, dataOffset, littleEndian) {
34919                 return dataView.getInt32(dataOffset, littleEndian);
34920             },
34921             size: 4
34922         },
34923         // srational, two slongs, first is numerator, second is denominator:
34924         10: {
34925             getValue: function (dataView, dataOffset, littleEndian) {
34926                 return dataView.getInt32(dataOffset, littleEndian) /
34927                     dataView.getInt32(dataOffset + 4, littleEndian);
34928             },
34929             size: 8
34930         }
34931     },
34932     
34933     footer : {
34934         STANDARD : [
34935             {
34936                 tag : 'div',
34937                 cls : 'btn-group roo-upload-cropbox-rotate-left',
34938                 action : 'rotate-left',
34939                 cn : [
34940                     {
34941                         tag : 'button',
34942                         cls : 'btn btn-default',
34943                         html : '<i class="fa fa-undo"></i>'
34944                     }
34945                 ]
34946             },
34947             {
34948                 tag : 'div',
34949                 cls : 'btn-group roo-upload-cropbox-picture',
34950                 action : 'picture',
34951                 cn : [
34952                     {
34953                         tag : 'button',
34954                         cls : 'btn btn-default',
34955                         html : '<i class="fa fa-picture-o"></i>'
34956                     }
34957                 ]
34958             },
34959             {
34960                 tag : 'div',
34961                 cls : 'btn-group roo-upload-cropbox-rotate-right',
34962                 action : 'rotate-right',
34963                 cn : [
34964                     {
34965                         tag : 'button',
34966                         cls : 'btn btn-default',
34967                         html : '<i class="fa fa-repeat"></i>'
34968                     }
34969                 ]
34970             }
34971         ],
34972         DOCUMENT : [
34973             {
34974                 tag : 'div',
34975                 cls : 'btn-group roo-upload-cropbox-rotate-left',
34976                 action : 'rotate-left',
34977                 cn : [
34978                     {
34979                         tag : 'button',
34980                         cls : 'btn btn-default',
34981                         html : '<i class="fa fa-undo"></i>'
34982                     }
34983                 ]
34984             },
34985             {
34986                 tag : 'div',
34987                 cls : 'btn-group roo-upload-cropbox-download',
34988                 action : 'download',
34989                 cn : [
34990                     {
34991                         tag : 'button',
34992                         cls : 'btn btn-default',
34993                         html : '<i class="fa fa-download"></i>'
34994                     }
34995                 ]
34996             },
34997             {
34998                 tag : 'div',
34999                 cls : 'btn-group roo-upload-cropbox-crop',
35000                 action : 'crop',
35001                 cn : [
35002                     {
35003                         tag : 'button',
35004                         cls : 'btn btn-default',
35005                         html : '<i class="fa fa-crop"></i>'
35006                     }
35007                 ]
35008             },
35009             {
35010                 tag : 'div',
35011                 cls : 'btn-group roo-upload-cropbox-trash',
35012                 action : 'trash',
35013                 cn : [
35014                     {
35015                         tag : 'button',
35016                         cls : 'btn btn-default',
35017                         html : '<i class="fa fa-trash"></i>'
35018                     }
35019                 ]
35020             },
35021             {
35022                 tag : 'div',
35023                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35024                 action : 'rotate-right',
35025                 cn : [
35026                     {
35027                         tag : 'button',
35028                         cls : 'btn btn-default',
35029                         html : '<i class="fa fa-repeat"></i>'
35030                     }
35031                 ]
35032             }
35033         ],
35034         ROTATOR : [
35035             {
35036                 tag : 'div',
35037                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35038                 action : 'rotate-left',
35039                 cn : [
35040                     {
35041                         tag : 'button',
35042                         cls : 'btn btn-default',
35043                         html : '<i class="fa fa-undo"></i>'
35044                     }
35045                 ]
35046             },
35047             {
35048                 tag : 'div',
35049                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35050                 action : 'rotate-right',
35051                 cn : [
35052                     {
35053                         tag : 'button',
35054                         cls : 'btn btn-default',
35055                         html : '<i class="fa fa-repeat"></i>'
35056                     }
35057                 ]
35058             }
35059         ]
35060     }
35061 });
35062
35063 /*
35064 * Licence: LGPL
35065 */
35066
35067 /**
35068  * @class Roo.bootstrap.DocumentManager
35069  * @extends Roo.bootstrap.Component
35070  * Bootstrap DocumentManager class
35071  * @cfg {String} paramName default 'imageUpload'
35072  * @cfg {String} toolTipName default 'filename'
35073  * @cfg {String} method default POST
35074  * @cfg {String} url action url
35075  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
35076  * @cfg {Boolean} multiple multiple upload default true
35077  * @cfg {Number} thumbSize default 300
35078  * @cfg {String} fieldLabel
35079  * @cfg {Number} labelWidth default 4
35080  * @cfg {String} labelAlign (left|top) default left
35081  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
35082 * @cfg {Number} labellg set the width of label (1-12)
35083  * @cfg {Number} labelmd set the width of label (1-12)
35084  * @cfg {Number} labelsm set the width of label (1-12)
35085  * @cfg {Number} labelxs set the width of label (1-12)
35086  * 
35087  * @constructor
35088  * Create a new DocumentManager
35089  * @param {Object} config The config object
35090  */
35091
35092 Roo.bootstrap.DocumentManager = function(config){
35093     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
35094     
35095     this.files = [];
35096     this.delegates = [];
35097     
35098     this.addEvents({
35099         /**
35100          * @event initial
35101          * Fire when initial the DocumentManager
35102          * @param {Roo.bootstrap.DocumentManager} this
35103          */
35104         "initial" : true,
35105         /**
35106          * @event inspect
35107          * inspect selected file
35108          * @param {Roo.bootstrap.DocumentManager} this
35109          * @param {File} file
35110          */
35111         "inspect" : true,
35112         /**
35113          * @event exception
35114          * Fire when xhr load exception
35115          * @param {Roo.bootstrap.DocumentManager} this
35116          * @param {XMLHttpRequest} xhr
35117          */
35118         "exception" : true,
35119         /**
35120          * @event afterupload
35121          * Fire when xhr load exception
35122          * @param {Roo.bootstrap.DocumentManager} this
35123          * @param {XMLHttpRequest} xhr
35124          */
35125         "afterupload" : true,
35126         /**
35127          * @event prepare
35128          * prepare the form data
35129          * @param {Roo.bootstrap.DocumentManager} this
35130          * @param {Object} formData
35131          */
35132         "prepare" : true,
35133         /**
35134          * @event remove
35135          * Fire when remove the file
35136          * @param {Roo.bootstrap.DocumentManager} this
35137          * @param {Object} file
35138          */
35139         "remove" : true,
35140         /**
35141          * @event refresh
35142          * Fire after refresh the file
35143          * @param {Roo.bootstrap.DocumentManager} this
35144          */
35145         "refresh" : true,
35146         /**
35147          * @event click
35148          * Fire after click the image
35149          * @param {Roo.bootstrap.DocumentManager} this
35150          * @param {Object} file
35151          */
35152         "click" : true,
35153         /**
35154          * @event edit
35155          * Fire when upload a image and editable set to true
35156          * @param {Roo.bootstrap.DocumentManager} this
35157          * @param {Object} file
35158          */
35159         "edit" : true,
35160         /**
35161          * @event beforeselectfile
35162          * Fire before select file
35163          * @param {Roo.bootstrap.DocumentManager} this
35164          */
35165         "beforeselectfile" : true,
35166         /**
35167          * @event process
35168          * Fire before process file
35169          * @param {Roo.bootstrap.DocumentManager} this
35170          * @param {Object} file
35171          */
35172         "process" : true,
35173         /**
35174          * @event previewrendered
35175          * Fire when preview rendered
35176          * @param {Roo.bootstrap.DocumentManager} this
35177          * @param {Object} file
35178          */
35179         "previewrendered" : true,
35180         /**
35181          */
35182         "previewResize" : true
35183         
35184     });
35185 };
35186
35187 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
35188     
35189     boxes : 0,
35190     inputName : '',
35191     thumbSize : 300,
35192     multiple : true,
35193     files : false,
35194     method : 'POST',
35195     url : '',
35196     paramName : 'imageUpload',
35197     toolTipName : 'filename',
35198     fieldLabel : '',
35199     labelWidth : 4,
35200     labelAlign : 'left',
35201     editable : true,
35202     delegates : false,
35203     xhr : false, 
35204     
35205     labellg : 0,
35206     labelmd : 0,
35207     labelsm : 0,
35208     labelxs : 0,
35209     
35210     getAutoCreate : function()
35211     {   
35212         var managerWidget = {
35213             tag : 'div',
35214             cls : 'roo-document-manager',
35215             cn : [
35216                 {
35217                     tag : 'input',
35218                     cls : 'roo-document-manager-selector',
35219                     type : 'file'
35220                 },
35221                 {
35222                     tag : 'div',
35223                     cls : 'roo-document-manager-uploader',
35224                     cn : [
35225                         {
35226                             tag : 'div',
35227                             cls : 'roo-document-manager-upload-btn',
35228                             html : '<i class="fa fa-plus"></i>'
35229                         }
35230                     ]
35231                     
35232                 }
35233             ]
35234         };
35235         
35236         var content = [
35237             {
35238                 tag : 'div',
35239                 cls : 'column col-md-12',
35240                 cn : managerWidget
35241             }
35242         ];
35243         
35244         if(this.fieldLabel.length){
35245             
35246             content = [
35247                 {
35248                     tag : 'div',
35249                     cls : 'column col-md-12',
35250                     html : this.fieldLabel
35251                 },
35252                 {
35253                     tag : 'div',
35254                     cls : 'column col-md-12',
35255                     cn : managerWidget
35256                 }
35257             ];
35258
35259             if(this.labelAlign == 'left'){
35260                 content = [
35261                     {
35262                         tag : 'div',
35263                         cls : 'column',
35264                         html : this.fieldLabel
35265                     },
35266                     {
35267                         tag : 'div',
35268                         cls : 'column',
35269                         cn : managerWidget
35270                     }
35271                 ];
35272                 
35273                 if(this.labelWidth > 12){
35274                     content[0].style = "width: " + this.labelWidth + 'px';
35275                 }
35276
35277                 if(this.labelWidth < 13 && this.labelmd == 0){
35278                     this.labelmd = this.labelWidth;
35279                 }
35280
35281                 if(this.labellg > 0){
35282                     content[0].cls += ' col-lg-' + this.labellg;
35283                     content[1].cls += ' col-lg-' + (12 - this.labellg);
35284                 }
35285
35286                 if(this.labelmd > 0){
35287                     content[0].cls += ' col-md-' + this.labelmd;
35288                     content[1].cls += ' col-md-' + (12 - this.labelmd);
35289                 }
35290
35291                 if(this.labelsm > 0){
35292                     content[0].cls += ' col-sm-' + this.labelsm;
35293                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
35294                 }
35295
35296                 if(this.labelxs > 0){
35297                     content[0].cls += ' col-xs-' + this.labelxs;
35298                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
35299                 }
35300                 
35301             }
35302         }
35303         
35304         var cfg = {
35305             tag : 'div',
35306             cls : 'row clearfix',
35307             cn : content
35308         };
35309         
35310         return cfg;
35311         
35312     },
35313     
35314     initEvents : function()
35315     {
35316         this.managerEl = this.el.select('.roo-document-manager', true).first();
35317         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35318         
35319         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
35320         this.selectorEl.hide();
35321         
35322         if(this.multiple){
35323             this.selectorEl.attr('multiple', 'multiple');
35324         }
35325         
35326         this.selectorEl.on('change', this.onFileSelected, this);
35327         
35328         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
35329         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35330         
35331         this.uploader.on('click', this.onUploaderClick, this);
35332         
35333         this.renderProgressDialog();
35334         
35335         var _this = this;
35336         
35337         window.addEventListener("resize", function() { _this.refresh(); } );
35338         
35339         this.fireEvent('initial', this);
35340     },
35341     
35342     renderProgressDialog : function()
35343     {
35344         var _this = this;
35345         
35346         this.progressDialog = new Roo.bootstrap.Modal({
35347             cls : 'roo-document-manager-progress-dialog',
35348             allow_close : false,
35349             animate : false,
35350             title : '',
35351             buttons : [
35352                 {
35353                     name  :'cancel',
35354                     weight : 'danger',
35355                     html : 'Cancel'
35356                 }
35357             ], 
35358             listeners : { 
35359                 btnclick : function() {
35360                     _this.uploadCancel();
35361                     this.hide();
35362                 }
35363             }
35364         });
35365          
35366         this.progressDialog.render(Roo.get(document.body));
35367          
35368         this.progress = new Roo.bootstrap.Progress({
35369             cls : 'roo-document-manager-progress',
35370             active : true,
35371             striped : true
35372         });
35373         
35374         this.progress.render(this.progressDialog.getChildContainer());
35375         
35376         this.progressBar = new Roo.bootstrap.ProgressBar({
35377             cls : 'roo-document-manager-progress-bar',
35378             aria_valuenow : 0,
35379             aria_valuemin : 0,
35380             aria_valuemax : 12,
35381             panel : 'success'
35382         });
35383         
35384         this.progressBar.render(this.progress.getChildContainer());
35385     },
35386     
35387     onUploaderClick : function(e)
35388     {
35389         e.preventDefault();
35390      
35391         if(this.fireEvent('beforeselectfile', this) != false){
35392             this.selectorEl.dom.click();
35393         }
35394         
35395     },
35396     
35397     onFileSelected : function(e)
35398     {
35399         e.preventDefault();
35400         
35401         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35402             return;
35403         }
35404         
35405         Roo.each(this.selectorEl.dom.files, function(file){
35406             if(this.fireEvent('inspect', this, file) != false){
35407                 this.files.push(file);
35408             }
35409         }, this);
35410         
35411         this.queue();
35412         
35413     },
35414     
35415     queue : function()
35416     {
35417         this.selectorEl.dom.value = '';
35418         
35419         if(!this.files || !this.files.length){
35420             return;
35421         }
35422         
35423         if(this.boxes > 0 && this.files.length > this.boxes){
35424             this.files = this.files.slice(0, this.boxes);
35425         }
35426         
35427         this.uploader.show();
35428         
35429         if(this.boxes > 0 && this.files.length > this.boxes - 1){
35430             this.uploader.hide();
35431         }
35432         
35433         var _this = this;
35434         
35435         var files = [];
35436         
35437         var docs = [];
35438         
35439         Roo.each(this.files, function(file){
35440             
35441             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35442                 var f = this.renderPreview(file);
35443                 files.push(f);
35444                 return;
35445             }
35446             
35447             if(file.type.indexOf('image') != -1){
35448                 this.delegates.push(
35449                     (function(){
35450                         _this.process(file);
35451                     }).createDelegate(this)
35452                 );
35453         
35454                 return;
35455             }
35456             
35457             docs.push(
35458                 (function(){
35459                     _this.process(file);
35460                 }).createDelegate(this)
35461             );
35462             
35463         }, this);
35464         
35465         this.files = files;
35466         
35467         this.delegates = this.delegates.concat(docs);
35468         
35469         if(!this.delegates.length){
35470             this.refresh();
35471             return;
35472         }
35473         
35474         this.progressBar.aria_valuemax = this.delegates.length;
35475         
35476         this.arrange();
35477         
35478         return;
35479     },
35480     
35481     arrange : function()
35482     {
35483         if(!this.delegates.length){
35484             this.progressDialog.hide();
35485             this.refresh();
35486             return;
35487         }
35488         
35489         var delegate = this.delegates.shift();
35490         
35491         this.progressDialog.show();
35492         
35493         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
35494         
35495         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
35496         
35497         delegate();
35498     },
35499     
35500     refresh : function()
35501     {
35502         this.uploader.show();
35503         
35504         if(this.boxes > 0 && this.files.length > this.boxes - 1){
35505             this.uploader.hide();
35506         }
35507         
35508         Roo.isTouch ? this.closable(false) : this.closable(true);
35509         
35510         this.fireEvent('refresh', this);
35511     },
35512     
35513     onRemove : function(e, el, o)
35514     {
35515         e.preventDefault();
35516         
35517         this.fireEvent('remove', this, o);
35518         
35519     },
35520     
35521     remove : function(o)
35522     {
35523         var files = [];
35524         
35525         Roo.each(this.files, function(file){
35526             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
35527                 files.push(file);
35528                 return;
35529             }
35530
35531             o.target.remove();
35532
35533         }, this);
35534         
35535         this.files = files;
35536         
35537         this.refresh();
35538     },
35539     
35540     clear : function()
35541     {
35542         Roo.each(this.files, function(file){
35543             if(!file.target){
35544                 return;
35545             }
35546             
35547             file.target.remove();
35548
35549         }, this);
35550         
35551         this.files = [];
35552         
35553         this.refresh();
35554     },
35555     
35556     onClick : function(e, el, o)
35557     {
35558         e.preventDefault();
35559         
35560         this.fireEvent('click', this, o);
35561         
35562     },
35563     
35564     closable : function(closable)
35565     {
35566         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
35567             
35568             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35569             
35570             if(closable){
35571                 el.show();
35572                 return;
35573             }
35574             
35575             el.hide();
35576             
35577         }, this);
35578     },
35579     
35580     xhrOnLoad : function(xhr)
35581     {
35582         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35583             el.remove();
35584         }, this);
35585         
35586         if (xhr.readyState !== 4) {
35587             this.arrange();
35588             this.fireEvent('exception', this, xhr);
35589             return;
35590         }
35591
35592         var response = Roo.decode(xhr.responseText);
35593         
35594         if(!response.success){
35595             this.arrange();
35596             this.fireEvent('exception', this, xhr);
35597             return;
35598         }
35599         
35600         var file = this.renderPreview(response.data);
35601         
35602         this.files.push(file);
35603         
35604         this.arrange();
35605         
35606         this.fireEvent('afterupload', this, xhr);
35607         
35608     },
35609     
35610     xhrOnError : function(xhr)
35611     {
35612         Roo.log('xhr on error');
35613         
35614         var response = Roo.decode(xhr.responseText);
35615           
35616         Roo.log(response);
35617         
35618         this.arrange();
35619     },
35620     
35621     process : function(file)
35622     {
35623         if(this.fireEvent('process', this, file) !== false){
35624             if(this.editable && file.type.indexOf('image') != -1){
35625                 this.fireEvent('edit', this, file);
35626                 return;
35627             }
35628
35629             this.uploadStart(file, false);
35630
35631             return;
35632         }
35633         
35634     },
35635     
35636     uploadStart : function(file, crop)
35637     {
35638         this.xhr = new XMLHttpRequest();
35639         
35640         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35641             this.arrange();
35642             return;
35643         }
35644         
35645         file.xhr = this.xhr;
35646             
35647         this.managerEl.createChild({
35648             tag : 'div',
35649             cls : 'roo-document-manager-loading',
35650             cn : [
35651                 {
35652                     tag : 'div',
35653                     tooltip : file.name,
35654                     cls : 'roo-document-manager-thumb',
35655                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
35656                 }
35657             ]
35658
35659         });
35660
35661         this.xhr.open(this.method, this.url, true);
35662         
35663         var headers = {
35664             "Accept": "application/json",
35665             "Cache-Control": "no-cache",
35666             "X-Requested-With": "XMLHttpRequest"
35667         };
35668         
35669         for (var headerName in headers) {
35670             var headerValue = headers[headerName];
35671             if (headerValue) {
35672                 this.xhr.setRequestHeader(headerName, headerValue);
35673             }
35674         }
35675         
35676         var _this = this;
35677         
35678         this.xhr.onload = function()
35679         {
35680             _this.xhrOnLoad(_this.xhr);
35681         }
35682         
35683         this.xhr.onerror = function()
35684         {
35685             _this.xhrOnError(_this.xhr);
35686         }
35687         
35688         var formData = new FormData();
35689
35690         formData.append('returnHTML', 'NO');
35691         
35692         if(crop){
35693             formData.append('crop', crop);
35694         }
35695         
35696         formData.append(this.paramName, file, file.name);
35697         
35698         var options = {
35699             file : file, 
35700             manually : false
35701         };
35702         
35703         if(this.fireEvent('prepare', this, formData, options) != false){
35704             
35705             if(options.manually){
35706                 return;
35707             }
35708             
35709             this.xhr.send(formData);
35710             return;
35711         };
35712         
35713         this.uploadCancel();
35714     },
35715     
35716     uploadCancel : function()
35717     {
35718         if (this.xhr) {
35719             this.xhr.abort();
35720         }
35721         
35722         this.delegates = [];
35723         
35724         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35725             el.remove();
35726         }, this);
35727         
35728         this.arrange();
35729     },
35730     
35731     renderPreview : function(file)
35732     {
35733         if(typeof(file.target) != 'undefined' && file.target){
35734             return file;
35735         }
35736         
35737         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
35738         
35739         var previewEl = this.managerEl.createChild({
35740             tag : 'div',
35741             cls : 'roo-document-manager-preview',
35742             cn : [
35743                 {
35744                     tag : 'div',
35745                     tooltip : file[this.toolTipName],
35746                     cls : 'roo-document-manager-thumb',
35747                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
35748                 },
35749                 {
35750                     tag : 'button',
35751                     cls : 'close',
35752                     html : '<i class="fa fa-times-circle"></i>'
35753                 }
35754             ]
35755         });
35756
35757         var close = previewEl.select('button.close', true).first();
35758
35759         close.on('click', this.onRemove, this, file);
35760
35761         file.target = previewEl;
35762
35763         var image = previewEl.select('img', true).first();
35764         
35765         var _this = this;
35766         
35767         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
35768         
35769         image.on('click', this.onClick, this, file);
35770         
35771         this.fireEvent('previewrendered', this, file);
35772         
35773         return file;
35774         
35775     },
35776     
35777     onPreviewLoad : function(file, image)
35778     {
35779         if(typeof(file.target) == 'undefined' || !file.target){
35780             return;
35781         }
35782         
35783         var width = image.dom.naturalWidth || image.dom.width;
35784         var height = image.dom.naturalHeight || image.dom.height;
35785         
35786         if(!this.previewResize) {
35787             return;
35788         }
35789         
35790         if(width > height){
35791             file.target.addClass('wide');
35792             return;
35793         }
35794         
35795         file.target.addClass('tall');
35796         return;
35797         
35798     },
35799     
35800     uploadFromSource : function(file, crop)
35801     {
35802         this.xhr = new XMLHttpRequest();
35803         
35804         this.managerEl.createChild({
35805             tag : 'div',
35806             cls : 'roo-document-manager-loading',
35807             cn : [
35808                 {
35809                     tag : 'div',
35810                     tooltip : file.name,
35811                     cls : 'roo-document-manager-thumb',
35812                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
35813                 }
35814             ]
35815
35816         });
35817
35818         this.xhr.open(this.method, this.url, true);
35819         
35820         var headers = {
35821             "Accept": "application/json",
35822             "Cache-Control": "no-cache",
35823             "X-Requested-With": "XMLHttpRequest"
35824         };
35825         
35826         for (var headerName in headers) {
35827             var headerValue = headers[headerName];
35828             if (headerValue) {
35829                 this.xhr.setRequestHeader(headerName, headerValue);
35830             }
35831         }
35832         
35833         var _this = this;
35834         
35835         this.xhr.onload = function()
35836         {
35837             _this.xhrOnLoad(_this.xhr);
35838         }
35839         
35840         this.xhr.onerror = function()
35841         {
35842             _this.xhrOnError(_this.xhr);
35843         }
35844         
35845         var formData = new FormData();
35846
35847         formData.append('returnHTML', 'NO');
35848         
35849         formData.append('crop', crop);
35850         
35851         if(typeof(file.filename) != 'undefined'){
35852             formData.append('filename', file.filename);
35853         }
35854         
35855         if(typeof(file.mimetype) != 'undefined'){
35856             formData.append('mimetype', file.mimetype);
35857         }
35858         
35859         Roo.log(formData);
35860         
35861         if(this.fireEvent('prepare', this, formData) != false){
35862             this.xhr.send(formData);
35863         };
35864     }
35865 });
35866
35867 /*
35868 * Licence: LGPL
35869 */
35870
35871 /**
35872  * @class Roo.bootstrap.DocumentViewer
35873  * @extends Roo.bootstrap.Component
35874  * Bootstrap DocumentViewer class
35875  * @cfg {Boolean} showDownload (true|false) show download button (default true)
35876  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
35877  * 
35878  * @constructor
35879  * Create a new DocumentViewer
35880  * @param {Object} config The config object
35881  */
35882
35883 Roo.bootstrap.DocumentViewer = function(config){
35884     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
35885     
35886     this.addEvents({
35887         /**
35888          * @event initial
35889          * Fire after initEvent
35890          * @param {Roo.bootstrap.DocumentViewer} this
35891          */
35892         "initial" : true,
35893         /**
35894          * @event click
35895          * Fire after click
35896          * @param {Roo.bootstrap.DocumentViewer} this
35897          */
35898         "click" : true,
35899         /**
35900          * @event download
35901          * Fire after download button
35902          * @param {Roo.bootstrap.DocumentViewer} this
35903          */
35904         "download" : true,
35905         /**
35906          * @event trash
35907          * Fire after trash button
35908          * @param {Roo.bootstrap.DocumentViewer} this
35909          */
35910         "trash" : true
35911         
35912     });
35913 };
35914
35915 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
35916     
35917     showDownload : true,
35918     
35919     showTrash : true,
35920     
35921     getAutoCreate : function()
35922     {
35923         var cfg = {
35924             tag : 'div',
35925             cls : 'roo-document-viewer',
35926             cn : [
35927                 {
35928                     tag : 'div',
35929                     cls : 'roo-document-viewer-body',
35930                     cn : [
35931                         {
35932                             tag : 'div',
35933                             cls : 'roo-document-viewer-thumb',
35934                             cn : [
35935                                 {
35936                                     tag : 'img',
35937                                     cls : 'roo-document-viewer-image'
35938                                 }
35939                             ]
35940                         }
35941                     ]
35942                 },
35943                 {
35944                     tag : 'div',
35945                     cls : 'roo-document-viewer-footer',
35946                     cn : {
35947                         tag : 'div',
35948                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
35949                         cn : [
35950                             {
35951                                 tag : 'div',
35952                                 cls : 'btn-group roo-document-viewer-download',
35953                                 cn : [
35954                                     {
35955                                         tag : 'button',
35956                                         cls : 'btn btn-default',
35957                                         html : '<i class="fa fa-download"></i>'
35958                                     }
35959                                 ]
35960                             },
35961                             {
35962                                 tag : 'div',
35963                                 cls : 'btn-group roo-document-viewer-trash',
35964                                 cn : [
35965                                     {
35966                                         tag : 'button',
35967                                         cls : 'btn btn-default',
35968                                         html : '<i class="fa fa-trash"></i>'
35969                                     }
35970                                 ]
35971                             }
35972                         ]
35973                     }
35974                 }
35975             ]
35976         };
35977         
35978         return cfg;
35979     },
35980     
35981     initEvents : function()
35982     {
35983         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
35984         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35985         
35986         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
35987         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35988         
35989         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
35990         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35991         
35992         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
35993         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
35994         
35995         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
35996         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
35997         
35998         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
35999         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
36000         
36001         this.bodyEl.on('click', this.onClick, this);
36002         this.downloadBtn.on('click', this.onDownload, this);
36003         this.trashBtn.on('click', this.onTrash, this);
36004         
36005         this.downloadBtn.hide();
36006         this.trashBtn.hide();
36007         
36008         if(this.showDownload){
36009             this.downloadBtn.show();
36010         }
36011         
36012         if(this.showTrash){
36013             this.trashBtn.show();
36014         }
36015         
36016         if(!this.showDownload && !this.showTrash) {
36017             this.footerEl.hide();
36018         }
36019         
36020     },
36021     
36022     initial : function()
36023     {
36024         this.fireEvent('initial', this);
36025         
36026     },
36027     
36028     onClick : function(e)
36029     {
36030         e.preventDefault();
36031         
36032         this.fireEvent('click', this);
36033     },
36034     
36035     onDownload : function(e)
36036     {
36037         e.preventDefault();
36038         
36039         this.fireEvent('download', this);
36040     },
36041     
36042     onTrash : function(e)
36043     {
36044         e.preventDefault();
36045         
36046         this.fireEvent('trash', this);
36047     }
36048     
36049 });
36050 /*
36051  * - LGPL
36052  *
36053  * FieldLabel
36054  * 
36055  */
36056
36057 /**
36058  * @class Roo.bootstrap.form.FieldLabel
36059  * @extends Roo.bootstrap.Component
36060  * Bootstrap FieldLabel class
36061  * @cfg {String} html contents of the element
36062  * @cfg {String} tag tag of the element default label
36063  * @cfg {String} cls class of the element
36064  * @cfg {String} target label target 
36065  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
36066  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
36067  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
36068  * @cfg {String} iconTooltip default "This field is required"
36069  * @cfg {String} indicatorpos (left|right) default left
36070  * 
36071  * @constructor
36072  * Create a new FieldLabel
36073  * @param {Object} config The config object
36074  */
36075
36076 Roo.bootstrap.form.FieldLabel = function(config){
36077     Roo.bootstrap.Element.superclass.constructor.call(this, config);
36078     
36079     this.addEvents({
36080             /**
36081              * @event invalid
36082              * Fires after the field has been marked as invalid.
36083              * @param {Roo.form.FieldLabel} this
36084              * @param {String} msg The validation message
36085              */
36086             invalid : true,
36087             /**
36088              * @event valid
36089              * Fires after the field has been validated with no errors.
36090              * @param {Roo.form.FieldLabel} this
36091              */
36092             valid : true
36093         });
36094 };
36095
36096 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
36097     
36098     tag: 'label',
36099     cls: '',
36100     html: '',
36101     target: '',
36102     allowBlank : true,
36103     invalidClass : 'has-warning',
36104     validClass : 'has-success',
36105     iconTooltip : 'This field is required',
36106     indicatorpos : 'left',
36107     
36108     getAutoCreate : function(){
36109         
36110         var cls = "";
36111         if (!this.allowBlank) {
36112             cls  = "visible";
36113         }
36114         
36115         var cfg = {
36116             tag : this.tag,
36117             cls : 'roo-bootstrap-field-label ' + this.cls,
36118             for : this.target,
36119             cn : [
36120                 {
36121                     tag : 'i',
36122                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
36123                     tooltip : this.iconTooltip
36124                 },
36125                 {
36126                     tag : 'span',
36127                     html : this.html
36128                 }
36129             ] 
36130         };
36131         
36132         if(this.indicatorpos == 'right'){
36133             var cfg = {
36134                 tag : this.tag,
36135                 cls : 'roo-bootstrap-field-label ' + this.cls,
36136                 for : this.target,
36137                 cn : [
36138                     {
36139                         tag : 'span',
36140                         html : this.html
36141                     },
36142                     {
36143                         tag : 'i',
36144                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
36145                         tooltip : this.iconTooltip
36146                     }
36147                 ] 
36148             };
36149         }
36150         
36151         return cfg;
36152     },
36153     
36154     initEvents: function() 
36155     {
36156         Roo.bootstrap.Element.superclass.initEvents.call(this);
36157         
36158         this.indicator = this.indicatorEl();
36159         
36160         if(this.indicator){
36161             this.indicator.removeClass('visible');
36162             this.indicator.addClass('invisible');
36163         }
36164         
36165         Roo.bootstrap.form.FieldLabel.register(this);
36166     },
36167     
36168     indicatorEl : function()
36169     {
36170         var indicator = this.el.select('i.roo-required-indicator',true).first();
36171         
36172         if(!indicator){
36173             return false;
36174         }
36175         
36176         return indicator;
36177         
36178     },
36179     
36180     /**
36181      * Mark this field as valid
36182      */
36183     markValid : function()
36184     {
36185         if(this.indicator){
36186             this.indicator.removeClass('visible');
36187             this.indicator.addClass('invisible');
36188         }
36189         if (Roo.bootstrap.version == 3) {
36190             this.el.removeClass(this.invalidClass);
36191             this.el.addClass(this.validClass);
36192         } else {
36193             this.el.removeClass('is-invalid');
36194             this.el.addClass('is-valid');
36195         }
36196         
36197         
36198         this.fireEvent('valid', this);
36199     },
36200     
36201     /**
36202      * Mark this field as invalid
36203      * @param {String} msg The validation message
36204      */
36205     markInvalid : function(msg)
36206     {
36207         if(this.indicator){
36208             this.indicator.removeClass('invisible');
36209             this.indicator.addClass('visible');
36210         }
36211           if (Roo.bootstrap.version == 3) {
36212             this.el.removeClass(this.validClass);
36213             this.el.addClass(this.invalidClass);
36214         } else {
36215             this.el.removeClass('is-valid');
36216             this.el.addClass('is-invalid');
36217         }
36218         
36219         
36220         this.fireEvent('invalid', this, msg);
36221     }
36222     
36223    
36224 });
36225
36226 Roo.apply(Roo.bootstrap.form.FieldLabel, {
36227     
36228     groups: {},
36229     
36230      /**
36231     * register a FieldLabel Group
36232     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
36233     */
36234     register : function(label)
36235     {
36236         if(this.groups.hasOwnProperty(label.target)){
36237             return;
36238         }
36239      
36240         this.groups[label.target] = label;
36241         
36242     },
36243     /**
36244     * fetch a FieldLabel Group based on the target
36245     * @param {string} target
36246     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
36247     */
36248     get: function(target) {
36249         if (typeof(this.groups[target]) == 'undefined') {
36250             return false;
36251         }
36252         
36253         return this.groups[target] ;
36254     }
36255 });
36256
36257  
36258
36259  /*
36260  * - LGPL
36261  *
36262  * page DateSplitField.
36263  * 
36264  */
36265
36266
36267 /**
36268  * @class Roo.bootstrap.form.DateSplitField
36269  * @extends Roo.bootstrap.Component
36270  * Bootstrap DateSplitField class
36271  * @cfg {string} fieldLabel - the label associated
36272  * @cfg {Number} labelWidth set the width of label (0-12)
36273  * @cfg {String} labelAlign (top|left)
36274  * @cfg {Boolean} dayAllowBlank (true|false) default false
36275  * @cfg {Boolean} monthAllowBlank (true|false) default false
36276  * @cfg {Boolean} yearAllowBlank (true|false) default false
36277  * @cfg {string} dayPlaceholder 
36278  * @cfg {string} monthPlaceholder
36279  * @cfg {string} yearPlaceholder
36280  * @cfg {string} dayFormat default 'd'
36281  * @cfg {string} monthFormat default 'm'
36282  * @cfg {string} yearFormat default 'Y'
36283  * @cfg {Number} labellg set the width of label (1-12)
36284  * @cfg {Number} labelmd set the width of label (1-12)
36285  * @cfg {Number} labelsm set the width of label (1-12)
36286  * @cfg {Number} labelxs set the width of label (1-12)
36287
36288  *     
36289  * @constructor
36290  * Create a new DateSplitField
36291  * @param {Object} config The config object
36292  */
36293
36294 Roo.bootstrap.form.DateSplitField = function(config){
36295     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
36296     
36297     this.addEvents({
36298         // raw events
36299          /**
36300          * @event years
36301          * getting the data of years
36302          * @param {Roo.bootstrap.form.DateSplitField} this
36303          * @param {Object} years
36304          */
36305         "years" : true,
36306         /**
36307          * @event days
36308          * getting the data of days
36309          * @param {Roo.bootstrap.form.DateSplitField} this
36310          * @param {Object} days
36311          */
36312         "days" : true,
36313         /**
36314          * @event invalid
36315          * Fires after the field has been marked as invalid.
36316          * @param {Roo.form.Field} this
36317          * @param {String} msg The validation message
36318          */
36319         invalid : true,
36320        /**
36321          * @event valid
36322          * Fires after the field has been validated with no errors.
36323          * @param {Roo.form.Field} this
36324          */
36325         valid : true
36326     });
36327 };
36328
36329 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
36330     
36331     fieldLabel : '',
36332     labelAlign : 'top',
36333     labelWidth : 3,
36334     dayAllowBlank : false,
36335     monthAllowBlank : false,
36336     yearAllowBlank : false,
36337     dayPlaceholder : '',
36338     monthPlaceholder : '',
36339     yearPlaceholder : '',
36340     dayFormat : 'd',
36341     monthFormat : 'm',
36342     yearFormat : 'Y',
36343     isFormField : true,
36344     labellg : 0,
36345     labelmd : 0,
36346     labelsm : 0,
36347     labelxs : 0,
36348     
36349     getAutoCreate : function()
36350     {
36351         var cfg = {
36352             tag : 'div',
36353             cls : 'row roo-date-split-field-group',
36354             cn : [
36355                 {
36356                     tag : 'input',
36357                     type : 'hidden',
36358                     cls : 'form-hidden-field roo-date-split-field-group-value',
36359                     name : this.name
36360                 }
36361             ]
36362         };
36363         
36364         var labelCls = 'col-md-12';
36365         var contentCls = 'col-md-4';
36366         
36367         if(this.fieldLabel){
36368             
36369             var label = {
36370                 tag : 'div',
36371                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
36372                 cn : [
36373                     {
36374                         tag : 'label',
36375                         html : this.fieldLabel
36376                     }
36377                 ]
36378             };
36379             
36380             if(this.labelAlign == 'left'){
36381             
36382                 if(this.labelWidth > 12){
36383                     label.style = "width: " + this.labelWidth + 'px';
36384                 }
36385
36386                 if(this.labelWidth < 13 && this.labelmd == 0){
36387                     this.labelmd = this.labelWidth;
36388                 }
36389
36390                 if(this.labellg > 0){
36391                     labelCls = ' col-lg-' + this.labellg;
36392                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
36393                 }
36394
36395                 if(this.labelmd > 0){
36396                     labelCls = ' col-md-' + this.labelmd;
36397                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
36398                 }
36399
36400                 if(this.labelsm > 0){
36401                     labelCls = ' col-sm-' + this.labelsm;
36402                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
36403                 }
36404
36405                 if(this.labelxs > 0){
36406                     labelCls = ' col-xs-' + this.labelxs;
36407                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
36408                 }
36409             }
36410             
36411             label.cls += ' ' + labelCls;
36412             
36413             cfg.cn.push(label);
36414         }
36415         
36416         Roo.each(['day', 'month', 'year'], function(t){
36417             cfg.cn.push({
36418                 tag : 'div',
36419                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
36420             });
36421         }, this);
36422         
36423         return cfg;
36424     },
36425     
36426     inputEl: function ()
36427     {
36428         return this.el.select('.roo-date-split-field-group-value', true).first();
36429     },
36430     
36431     onRender : function(ct, position) 
36432     {
36433         var _this = this;
36434         
36435         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
36436         
36437         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
36438         
36439         this.dayField = new Roo.bootstrap.form.ComboBox({
36440             allowBlank : this.dayAllowBlank,
36441             alwaysQuery : true,
36442             displayField : 'value',
36443             editable : false,
36444             fieldLabel : '',
36445             forceSelection : true,
36446             mode : 'local',
36447             placeholder : this.dayPlaceholder,
36448             selectOnFocus : true,
36449             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36450             triggerAction : 'all',
36451             typeAhead : true,
36452             valueField : 'value',
36453             store : new Roo.data.SimpleStore({
36454                 data : (function() {    
36455                     var days = [];
36456                     _this.fireEvent('days', _this, days);
36457                     return days;
36458                 })(),
36459                 fields : [ 'value' ]
36460             }),
36461             listeners : {
36462                 select : function (_self, record, index)
36463                 {
36464                     _this.setValue(_this.getValue());
36465                 }
36466             }
36467         });
36468
36469         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
36470         
36471         this.monthField = new Roo.bootstrap.form.MonthField({
36472             after : '<i class=\"fa fa-calendar\"></i>',
36473             allowBlank : this.monthAllowBlank,
36474             placeholder : this.monthPlaceholder,
36475             readOnly : true,
36476             listeners : {
36477                 render : function (_self)
36478                 {
36479                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
36480                         e.preventDefault();
36481                         _self.focus();
36482                     });
36483                 },
36484                 select : function (_self, oldvalue, newvalue)
36485                 {
36486                     _this.setValue(_this.getValue());
36487                 }
36488             }
36489         });
36490         
36491         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
36492         
36493         this.yearField = new Roo.bootstrap.form.ComboBox({
36494             allowBlank : this.yearAllowBlank,
36495             alwaysQuery : true,
36496             displayField : 'value',
36497             editable : false,
36498             fieldLabel : '',
36499             forceSelection : true,
36500             mode : 'local',
36501             placeholder : this.yearPlaceholder,
36502             selectOnFocus : true,
36503             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36504             triggerAction : 'all',
36505             typeAhead : true,
36506             valueField : 'value',
36507             store : new Roo.data.SimpleStore({
36508                 data : (function() {
36509                     var years = [];
36510                     _this.fireEvent('years', _this, years);
36511                     return years;
36512                 })(),
36513                 fields : [ 'value' ]
36514             }),
36515             listeners : {
36516                 select : function (_self, record, index)
36517                 {
36518                     _this.setValue(_this.getValue());
36519                 }
36520             }
36521         });
36522
36523         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
36524     },
36525     
36526     setValue : function(v, format)
36527     {
36528         this.inputEl.dom.value = v;
36529         
36530         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
36531         
36532         var d = Date.parseDate(v, f);
36533         
36534         if(!d){
36535             this.validate();
36536             return;
36537         }
36538         
36539         this.setDay(d.format(this.dayFormat));
36540         this.setMonth(d.format(this.monthFormat));
36541         this.setYear(d.format(this.yearFormat));
36542         
36543         this.validate();
36544         
36545         return;
36546     },
36547     
36548     setDay : function(v)
36549     {
36550         this.dayField.setValue(v);
36551         this.inputEl.dom.value = this.getValue();
36552         this.validate();
36553         return;
36554     },
36555     
36556     setMonth : function(v)
36557     {
36558         this.monthField.setValue(v, true);
36559         this.inputEl.dom.value = this.getValue();
36560         this.validate();
36561         return;
36562     },
36563     
36564     setYear : function(v)
36565     {
36566         this.yearField.setValue(v);
36567         this.inputEl.dom.value = this.getValue();
36568         this.validate();
36569         return;
36570     },
36571     
36572     getDay : function()
36573     {
36574         return this.dayField.getValue();
36575     },
36576     
36577     getMonth : function()
36578     {
36579         return this.monthField.getValue();
36580     },
36581     
36582     getYear : function()
36583     {
36584         return this.yearField.getValue();
36585     },
36586     
36587     getValue : function()
36588     {
36589         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
36590         
36591         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
36592         
36593         return date;
36594     },
36595     
36596     reset : function()
36597     {
36598         this.setDay('');
36599         this.setMonth('');
36600         this.setYear('');
36601         this.inputEl.dom.value = '';
36602         this.validate();
36603         return;
36604     },
36605     
36606     validate : function()
36607     {
36608         var d = this.dayField.validate();
36609         var m = this.monthField.validate();
36610         var y = this.yearField.validate();
36611         
36612         var valid = true;
36613         
36614         if(
36615                 (!this.dayAllowBlank && !d) ||
36616                 (!this.monthAllowBlank && !m) ||
36617                 (!this.yearAllowBlank && !y)
36618         ){
36619             valid = false;
36620         }
36621         
36622         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
36623             return valid;
36624         }
36625         
36626         if(valid){
36627             this.markValid();
36628             return valid;
36629         }
36630         
36631         this.markInvalid();
36632         
36633         return valid;
36634     },
36635     
36636     markValid : function()
36637     {
36638         
36639         var label = this.el.select('label', true).first();
36640         var icon = this.el.select('i.fa-star', true).first();
36641
36642         if(label && icon){
36643             icon.remove();
36644         }
36645         
36646         this.fireEvent('valid', this);
36647     },
36648     
36649      /**
36650      * Mark this field as invalid
36651      * @param {String} msg The validation message
36652      */
36653     markInvalid : function(msg)
36654     {
36655         
36656         var label = this.el.select('label', true).first();
36657         var icon = this.el.select('i.fa-star', true).first();
36658
36659         if(label && !icon){
36660             this.el.select('.roo-date-split-field-label', true).createChild({
36661                 tag : 'i',
36662                 cls : 'text-danger fa fa-lg fa-star',
36663                 tooltip : 'This field is required',
36664                 style : 'margin-right:5px;'
36665             }, label, true);
36666         }
36667         
36668         this.fireEvent('invalid', this, msg);
36669     },
36670     
36671     clearInvalid : function()
36672     {
36673         var label = this.el.select('label', true).first();
36674         var icon = this.el.select('i.fa-star', true).first();
36675
36676         if(label && icon){
36677             icon.remove();
36678         }
36679         
36680         this.fireEvent('valid', this);
36681     },
36682     
36683     getName: function()
36684     {
36685         return this.name;
36686     }
36687     
36688 });
36689
36690  
36691
36692 /**
36693  * @class Roo.bootstrap.LayoutMasonry
36694  * @extends Roo.bootstrap.Component
36695  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
36696  * Bootstrap Layout Masonry class
36697  *
36698  * This is based on 
36699  * http://masonry.desandro.com
36700  *
36701  * The idea is to render all the bricks based on vertical width...
36702  *
36703  * The original code extends 'outlayer' - we might need to use that....
36704
36705  * @constructor
36706  * Create a new Element
36707  * @param {Object} config The config object
36708  */
36709
36710 Roo.bootstrap.LayoutMasonry = function(config){
36711     
36712     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
36713     
36714     this.bricks = [];
36715     
36716     Roo.bootstrap.LayoutMasonry.register(this);
36717     
36718     this.addEvents({
36719         // raw events
36720         /**
36721          * @event layout
36722          * Fire after layout the items
36723          * @param {Roo.bootstrap.LayoutMasonry} this
36724          * @param {Roo.EventObject} e
36725          */
36726         "layout" : true
36727     });
36728     
36729 };
36730
36731 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
36732     
36733     /**
36734      * @cfg {Boolean} isLayoutInstant = no animation?
36735      */   
36736     isLayoutInstant : false, // needed?
36737    
36738     /**
36739      * @cfg {Number} boxWidth  width of the columns
36740      */   
36741     boxWidth : 450,
36742     
36743       /**
36744      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
36745      */   
36746     boxHeight : 0,
36747     
36748     /**
36749      * @cfg {Number} padWidth padding below box..
36750      */   
36751     padWidth : 10, 
36752     
36753     /**
36754      * @cfg {Number} gutter gutter width..
36755      */   
36756     gutter : 10,
36757     
36758      /**
36759      * @cfg {Number} maxCols maximum number of columns
36760      */   
36761     
36762     maxCols: 0,
36763     
36764     /**
36765      * @cfg {Boolean} isAutoInitial defalut true
36766      */   
36767     isAutoInitial : true, 
36768     
36769     containerWidth: 0,
36770     
36771     /**
36772      * @cfg {Boolean} isHorizontal defalut false
36773      */   
36774     isHorizontal : false, 
36775
36776     currentSize : null,
36777     
36778     tag: 'div',
36779     
36780     cls: '',
36781     
36782     bricks: null, //CompositeElement
36783     
36784     cols : 1,
36785     
36786     _isLayoutInited : false,
36787     
36788 //    isAlternative : false, // only use for vertical layout...
36789     
36790     /**
36791      * @cfg {Number} alternativePadWidth padding below box..
36792      */   
36793     alternativePadWidth : 50,
36794     
36795     selectedBrick : [],
36796     
36797     getAutoCreate : function(){
36798         
36799         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
36800         
36801         var cfg = {
36802             tag: this.tag,
36803             cls: 'blog-masonary-wrapper ' + this.cls,
36804             cn : {
36805                 cls : 'mas-boxes masonary'
36806             }
36807         };
36808         
36809         return cfg;
36810     },
36811     
36812     getChildContainer: function( )
36813     {
36814         if (this.boxesEl) {
36815             return this.boxesEl;
36816         }
36817         
36818         this.boxesEl = this.el.select('.mas-boxes').first();
36819         
36820         return this.boxesEl;
36821     },
36822     
36823     
36824     initEvents : function()
36825     {
36826         var _this = this;
36827         
36828         if(this.isAutoInitial){
36829             Roo.log('hook children rendered');
36830             this.on('childrenrendered', function() {
36831                 Roo.log('children rendered');
36832                 _this.initial();
36833             } ,this);
36834         }
36835     },
36836     
36837     initial : function()
36838     {
36839         this.selectedBrick = [];
36840         
36841         this.currentSize = this.el.getBox(true);
36842         
36843         Roo.EventManager.onWindowResize(this.resize, this); 
36844
36845         if(!this.isAutoInitial){
36846             this.layout();
36847             return;
36848         }
36849         
36850         this.layout();
36851         
36852         return;
36853         //this.layout.defer(500,this);
36854         
36855     },
36856     
36857     resize : function()
36858     {
36859         var cs = this.el.getBox(true);
36860         
36861         if (
36862                 this.currentSize.width == cs.width && 
36863                 this.currentSize.x == cs.x && 
36864                 this.currentSize.height == cs.height && 
36865                 this.currentSize.y == cs.y 
36866         ) {
36867             Roo.log("no change in with or X or Y");
36868             return;
36869         }
36870         
36871         this.currentSize = cs;
36872         
36873         this.layout();
36874         
36875     },
36876     
36877     layout : function()
36878     {   
36879         this._resetLayout();
36880         
36881         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
36882         
36883         this.layoutItems( isInstant );
36884       
36885         this._isLayoutInited = true;
36886         
36887         this.fireEvent('layout', this);
36888         
36889     },
36890     
36891     _resetLayout : function()
36892     {
36893         if(this.isHorizontal){
36894             this.horizontalMeasureColumns();
36895             return;
36896         }
36897         
36898         this.verticalMeasureColumns();
36899         
36900     },
36901     
36902     verticalMeasureColumns : function()
36903     {
36904         this.getContainerWidth();
36905         
36906 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
36907 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
36908 //            return;
36909 //        }
36910         
36911         var boxWidth = this.boxWidth + this.padWidth;
36912         
36913         if(this.containerWidth < this.boxWidth){
36914             boxWidth = this.containerWidth
36915         }
36916         
36917         var containerWidth = this.containerWidth;
36918         
36919         var cols = Math.floor(containerWidth / boxWidth);
36920         
36921         this.cols = Math.max( cols, 1 );
36922         
36923         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36924         
36925         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
36926         
36927         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
36928         
36929         this.colWidth = boxWidth + avail - this.padWidth;
36930         
36931         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
36932         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
36933     },
36934     
36935     horizontalMeasureColumns : function()
36936     {
36937         this.getContainerWidth();
36938         
36939         var boxWidth = this.boxWidth;
36940         
36941         if(this.containerWidth < boxWidth){
36942             boxWidth = this.containerWidth;
36943         }
36944         
36945         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
36946         
36947         this.el.setHeight(boxWidth);
36948         
36949     },
36950     
36951     getContainerWidth : function()
36952     {
36953         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36954     },
36955     
36956     layoutItems : function( isInstant )
36957     {
36958         Roo.log(this.bricks);
36959         
36960         var items = Roo.apply([], this.bricks);
36961         
36962         if(this.isHorizontal){
36963             this._horizontalLayoutItems( items , isInstant );
36964             return;
36965         }
36966         
36967 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
36968 //            this._verticalAlternativeLayoutItems( items , isInstant );
36969 //            return;
36970 //        }
36971         
36972         this._verticalLayoutItems( items , isInstant );
36973         
36974     },
36975     
36976     _verticalLayoutItems : function ( items , isInstant)
36977     {
36978         if ( !items || !items.length ) {
36979             return;
36980         }
36981         
36982         var standard = [
36983             ['xs', 'xs', 'xs', 'tall'],
36984             ['xs', 'xs', 'tall'],
36985             ['xs', 'xs', 'sm'],
36986             ['xs', 'xs', 'xs'],
36987             ['xs', 'tall'],
36988             ['xs', 'sm'],
36989             ['xs', 'xs'],
36990             ['xs'],
36991             
36992             ['sm', 'xs', 'xs'],
36993             ['sm', 'xs'],
36994             ['sm'],
36995             
36996             ['tall', 'xs', 'xs', 'xs'],
36997             ['tall', 'xs', 'xs'],
36998             ['tall', 'xs'],
36999             ['tall']
37000             
37001         ];
37002         
37003         var queue = [];
37004         
37005         var boxes = [];
37006         
37007         var box = [];
37008         
37009         Roo.each(items, function(item, k){
37010             
37011             switch (item.size) {
37012                 // these layouts take up a full box,
37013                 case 'md' :
37014                 case 'md-left' :
37015                 case 'md-right' :
37016                 case 'wide' :
37017                     
37018                     if(box.length){
37019                         boxes.push(box);
37020                         box = [];
37021                     }
37022                     
37023                     boxes.push([item]);
37024                     
37025                     break;
37026                     
37027                 case 'xs' :
37028                 case 'sm' :
37029                 case 'tall' :
37030                     
37031                     box.push(item);
37032                     
37033                     break;
37034                 default :
37035                     break;
37036                     
37037             }
37038             
37039         }, this);
37040         
37041         if(box.length){
37042             boxes.push(box);
37043             box = [];
37044         }
37045         
37046         var filterPattern = function(box, length)
37047         {
37048             if(!box.length){
37049                 return;
37050             }
37051             
37052             var match = false;
37053             
37054             var pattern = box.slice(0, length);
37055             
37056             var format = [];
37057             
37058             Roo.each(pattern, function(i){
37059                 format.push(i.size);
37060             }, this);
37061             
37062             Roo.each(standard, function(s){
37063                 
37064                 if(String(s) != String(format)){
37065                     return;
37066                 }
37067                 
37068                 match = true;
37069                 return false;
37070                 
37071             }, this);
37072             
37073             if(!match && length == 1){
37074                 return;
37075             }
37076             
37077             if(!match){
37078                 filterPattern(box, length - 1);
37079                 return;
37080             }
37081                 
37082             queue.push(pattern);
37083
37084             box = box.slice(length, box.length);
37085
37086             filterPattern(box, 4);
37087
37088             return;
37089             
37090         }
37091         
37092         Roo.each(boxes, function(box, k){
37093             
37094             if(!box.length){
37095                 return;
37096             }
37097             
37098             if(box.length == 1){
37099                 queue.push(box);
37100                 return;
37101             }
37102             
37103             filterPattern(box, 4);
37104             
37105         }, this);
37106         
37107         this._processVerticalLayoutQueue( queue, isInstant );
37108         
37109     },
37110     
37111 //    _verticalAlternativeLayoutItems : function( items , isInstant )
37112 //    {
37113 //        if ( !items || !items.length ) {
37114 //            return;
37115 //        }
37116 //
37117 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
37118 //        
37119 //    },
37120     
37121     _horizontalLayoutItems : function ( items , isInstant)
37122     {
37123         if ( !items || !items.length || items.length < 3) {
37124             return;
37125         }
37126         
37127         items.reverse();
37128         
37129         var eItems = items.slice(0, 3);
37130         
37131         items = items.slice(3, items.length);
37132         
37133         var standard = [
37134             ['xs', 'xs', 'xs', 'wide'],
37135             ['xs', 'xs', 'wide'],
37136             ['xs', 'xs', 'sm'],
37137             ['xs', 'xs', 'xs'],
37138             ['xs', 'wide'],
37139             ['xs', 'sm'],
37140             ['xs', 'xs'],
37141             ['xs'],
37142             
37143             ['sm', 'xs', 'xs'],
37144             ['sm', 'xs'],
37145             ['sm'],
37146             
37147             ['wide', 'xs', 'xs', 'xs'],
37148             ['wide', 'xs', 'xs'],
37149             ['wide', 'xs'],
37150             ['wide'],
37151             
37152             ['wide-thin']
37153         ];
37154         
37155         var queue = [];
37156         
37157         var boxes = [];
37158         
37159         var box = [];
37160         
37161         Roo.each(items, function(item, k){
37162             
37163             switch (item.size) {
37164                 case 'md' :
37165                 case 'md-left' :
37166                 case 'md-right' :
37167                 case 'tall' :
37168                     
37169                     if(box.length){
37170                         boxes.push(box);
37171                         box = [];
37172                     }
37173                     
37174                     boxes.push([item]);
37175                     
37176                     break;
37177                     
37178                 case 'xs' :
37179                 case 'sm' :
37180                 case 'wide' :
37181                 case 'wide-thin' :
37182                     
37183                     box.push(item);
37184                     
37185                     break;
37186                 default :
37187                     break;
37188                     
37189             }
37190             
37191         }, this);
37192         
37193         if(box.length){
37194             boxes.push(box);
37195             box = [];
37196         }
37197         
37198         var filterPattern = function(box, length)
37199         {
37200             if(!box.length){
37201                 return;
37202             }
37203             
37204             var match = false;
37205             
37206             var pattern = box.slice(0, length);
37207             
37208             var format = [];
37209             
37210             Roo.each(pattern, function(i){
37211                 format.push(i.size);
37212             }, this);
37213             
37214             Roo.each(standard, function(s){
37215                 
37216                 if(String(s) != String(format)){
37217                     return;
37218                 }
37219                 
37220                 match = true;
37221                 return false;
37222                 
37223             }, this);
37224             
37225             if(!match && length == 1){
37226                 return;
37227             }
37228             
37229             if(!match){
37230                 filterPattern(box, length - 1);
37231                 return;
37232             }
37233                 
37234             queue.push(pattern);
37235
37236             box = box.slice(length, box.length);
37237
37238             filterPattern(box, 4);
37239
37240             return;
37241             
37242         }
37243         
37244         Roo.each(boxes, function(box, k){
37245             
37246             if(!box.length){
37247                 return;
37248             }
37249             
37250             if(box.length == 1){
37251                 queue.push(box);
37252                 return;
37253             }
37254             
37255             filterPattern(box, 4);
37256             
37257         }, this);
37258         
37259         
37260         var prune = [];
37261         
37262         var pos = this.el.getBox(true);
37263         
37264         var minX = pos.x;
37265         
37266         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37267         
37268         var hit_end = false;
37269         
37270         Roo.each(queue, function(box){
37271             
37272             if(hit_end){
37273                 
37274                 Roo.each(box, function(b){
37275                 
37276                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
37277                     b.el.hide();
37278
37279                 }, this);
37280
37281                 return;
37282             }
37283             
37284             var mx = 0;
37285             
37286             Roo.each(box, function(b){
37287                 
37288                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
37289                 b.el.show();
37290
37291                 mx = Math.max(mx, b.x);
37292                 
37293             }, this);
37294             
37295             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
37296             
37297             if(maxX < minX){
37298                 
37299                 Roo.each(box, function(b){
37300                 
37301                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
37302                     b.el.hide();
37303                     
37304                 }, this);
37305                 
37306                 hit_end = true;
37307                 
37308                 return;
37309             }
37310             
37311             prune.push(box);
37312             
37313         }, this);
37314         
37315         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
37316     },
37317     
37318     /** Sets position of item in DOM
37319     * @param {Element} item
37320     * @param {Number} x - horizontal position
37321     * @param {Number} y - vertical position
37322     * @param {Boolean} isInstant - disables transitions
37323     */
37324     _processVerticalLayoutQueue : function( queue, isInstant )
37325     {
37326         var pos = this.el.getBox(true);
37327         var x = pos.x;
37328         var y = pos.y;
37329         var maxY = [];
37330         
37331         for (var i = 0; i < this.cols; i++){
37332             maxY[i] = pos.y;
37333         }
37334         
37335         Roo.each(queue, function(box, k){
37336             
37337             var col = k % this.cols;
37338             
37339             Roo.each(box, function(b,kk){
37340                 
37341                 b.el.position('absolute');
37342                 
37343                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37344                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37345                 
37346                 if(b.size == 'md-left' || b.size == 'md-right'){
37347                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37348                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37349                 }
37350                 
37351                 b.el.setWidth(width);
37352                 b.el.setHeight(height);
37353                 // iframe?
37354                 b.el.select('iframe',true).setSize(width,height);
37355                 
37356             }, this);
37357             
37358             for (var i = 0; i < this.cols; i++){
37359                 
37360                 if(maxY[i] < maxY[col]){
37361                     col = i;
37362                     continue;
37363                 }
37364                 
37365                 col = Math.min(col, i);
37366                 
37367             }
37368             
37369             x = pos.x + col * (this.colWidth + this.padWidth);
37370             
37371             y = maxY[col];
37372             
37373             var positions = [];
37374             
37375             switch (box.length){
37376                 case 1 :
37377                     positions = this.getVerticalOneBoxColPositions(x, y, box);
37378                     break;
37379                 case 2 :
37380                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
37381                     break;
37382                 case 3 :
37383                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
37384                     break;
37385                 case 4 :
37386                     positions = this.getVerticalFourBoxColPositions(x, y, box);
37387                     break;
37388                 default :
37389                     break;
37390             }
37391             
37392             Roo.each(box, function(b,kk){
37393                 
37394                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37395                 
37396                 var sz = b.el.getSize();
37397                 
37398                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
37399                 
37400             }, this);
37401             
37402         }, this);
37403         
37404         var mY = 0;
37405         
37406         for (var i = 0; i < this.cols; i++){
37407             mY = Math.max(mY, maxY[i]);
37408         }
37409         
37410         this.el.setHeight(mY - pos.y);
37411         
37412     },
37413     
37414 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
37415 //    {
37416 //        var pos = this.el.getBox(true);
37417 //        var x = pos.x;
37418 //        var y = pos.y;
37419 //        var maxX = pos.right;
37420 //        
37421 //        var maxHeight = 0;
37422 //        
37423 //        Roo.each(items, function(item, k){
37424 //            
37425 //            var c = k % 2;
37426 //            
37427 //            item.el.position('absolute');
37428 //                
37429 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
37430 //
37431 //            item.el.setWidth(width);
37432 //
37433 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
37434 //
37435 //            item.el.setHeight(height);
37436 //            
37437 //            if(c == 0){
37438 //                item.el.setXY([x, y], isInstant ? false : true);
37439 //            } else {
37440 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
37441 //            }
37442 //            
37443 //            y = y + height + this.alternativePadWidth;
37444 //            
37445 //            maxHeight = maxHeight + height + this.alternativePadWidth;
37446 //            
37447 //        }, this);
37448 //        
37449 //        this.el.setHeight(maxHeight);
37450 //        
37451 //    },
37452     
37453     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
37454     {
37455         var pos = this.el.getBox(true);
37456         
37457         var minX = pos.x;
37458         var minY = pos.y;
37459         
37460         var maxX = pos.right;
37461         
37462         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
37463         
37464         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37465         
37466         Roo.each(queue, function(box, k){
37467             
37468             Roo.each(box, function(b, kk){
37469                 
37470                 b.el.position('absolute');
37471                 
37472                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37473                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37474                 
37475                 if(b.size == 'md-left' || b.size == 'md-right'){
37476                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37477                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37478                 }
37479                 
37480                 b.el.setWidth(width);
37481                 b.el.setHeight(height);
37482                 
37483             }, this);
37484             
37485             if(!box.length){
37486                 return;
37487             }
37488             
37489             var positions = [];
37490             
37491             switch (box.length){
37492                 case 1 :
37493                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
37494                     break;
37495                 case 2 :
37496                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
37497                     break;
37498                 case 3 :
37499                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
37500                     break;
37501                 case 4 :
37502                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
37503                     break;
37504                 default :
37505                     break;
37506             }
37507             
37508             Roo.each(box, function(b,kk){
37509                 
37510                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37511                 
37512                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
37513                 
37514             }, this);
37515             
37516         }, this);
37517         
37518     },
37519     
37520     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
37521     {
37522         Roo.each(eItems, function(b,k){
37523             
37524             b.size = (k == 0) ? 'sm' : 'xs';
37525             b.x = (k == 0) ? 2 : 1;
37526             b.y = (k == 0) ? 2 : 1;
37527             
37528             b.el.position('absolute');
37529             
37530             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37531                 
37532             b.el.setWidth(width);
37533             
37534             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37535             
37536             b.el.setHeight(height);
37537             
37538         }, this);
37539
37540         var positions = [];
37541         
37542         positions.push({
37543             x : maxX - this.unitWidth * 2 - this.gutter,
37544             y : minY
37545         });
37546         
37547         positions.push({
37548             x : maxX - this.unitWidth,
37549             y : minY + (this.unitWidth + this.gutter) * 2
37550         });
37551         
37552         positions.push({
37553             x : maxX - this.unitWidth * 3 - this.gutter * 2,
37554             y : minY
37555         });
37556         
37557         Roo.each(eItems, function(b,k){
37558             
37559             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
37560
37561         }, this);
37562         
37563     },
37564     
37565     getVerticalOneBoxColPositions : function(x, y, box)
37566     {
37567         var pos = [];
37568         
37569         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
37570         
37571         if(box[0].size == 'md-left'){
37572             rand = 0;
37573         }
37574         
37575         if(box[0].size == 'md-right'){
37576             rand = 1;
37577         }
37578         
37579         pos.push({
37580             x : x + (this.unitWidth + this.gutter) * rand,
37581             y : y
37582         });
37583         
37584         return pos;
37585     },
37586     
37587     getVerticalTwoBoxColPositions : function(x, y, box)
37588     {
37589         var pos = [];
37590         
37591         if(box[0].size == 'xs'){
37592             
37593             pos.push({
37594                 x : x,
37595                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
37596             });
37597
37598             pos.push({
37599                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
37600                 y : y
37601             });
37602             
37603             return pos;
37604             
37605         }
37606         
37607         pos.push({
37608             x : x,
37609             y : y
37610         });
37611
37612         pos.push({
37613             x : x + (this.unitWidth + this.gutter) * 2,
37614             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
37615         });
37616         
37617         return pos;
37618         
37619     },
37620     
37621     getVerticalThreeBoxColPositions : function(x, y, box)
37622     {
37623         var pos = [];
37624         
37625         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
37626             
37627             pos.push({
37628                 x : x,
37629                 y : y
37630             });
37631
37632             pos.push({
37633                 x : x + (this.unitWidth + this.gutter) * 1,
37634                 y : y
37635             });
37636             
37637             pos.push({
37638                 x : x + (this.unitWidth + this.gutter) * 2,
37639                 y : y
37640             });
37641             
37642             return pos;
37643             
37644         }
37645         
37646         if(box[0].size == 'xs' && box[1].size == 'xs'){
37647             
37648             pos.push({
37649                 x : x,
37650                 y : y
37651             });
37652
37653             pos.push({
37654                 x : x,
37655                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
37656             });
37657             
37658             pos.push({
37659                 x : x + (this.unitWidth + this.gutter) * 1,
37660                 y : y
37661             });
37662             
37663             return pos;
37664             
37665         }
37666         
37667         pos.push({
37668             x : x,
37669             y : y
37670         });
37671
37672         pos.push({
37673             x : x + (this.unitWidth + this.gutter) * 2,
37674             y : y
37675         });
37676
37677         pos.push({
37678             x : x + (this.unitWidth + this.gutter) * 2,
37679             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
37680         });
37681             
37682         return pos;
37683         
37684     },
37685     
37686     getVerticalFourBoxColPositions : function(x, y, box)
37687     {
37688         var pos = [];
37689         
37690         if(box[0].size == 'xs'){
37691             
37692             pos.push({
37693                 x : x,
37694                 y : y
37695             });
37696
37697             pos.push({
37698                 x : x,
37699                 y : y + (this.unitHeight + this.gutter) * 1
37700             });
37701             
37702             pos.push({
37703                 x : x,
37704                 y : y + (this.unitHeight + this.gutter) * 2
37705             });
37706             
37707             pos.push({
37708                 x : x + (this.unitWidth + this.gutter) * 1,
37709                 y : y
37710             });
37711             
37712             return pos;
37713             
37714         }
37715         
37716         pos.push({
37717             x : x,
37718             y : y
37719         });
37720
37721         pos.push({
37722             x : x + (this.unitWidth + this.gutter) * 2,
37723             y : y
37724         });
37725
37726         pos.push({
37727             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
37728             y : y + (this.unitHeight + this.gutter) * 1
37729         });
37730
37731         pos.push({
37732             x : x + (this.unitWidth + this.gutter) * 2,
37733             y : y + (this.unitWidth + this.gutter) * 2
37734         });
37735
37736         return pos;
37737         
37738     },
37739     
37740     getHorizontalOneBoxColPositions : function(maxX, minY, box)
37741     {
37742         var pos = [];
37743         
37744         if(box[0].size == 'md-left'){
37745             pos.push({
37746                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37747                 y : minY
37748             });
37749             
37750             return pos;
37751         }
37752         
37753         if(box[0].size == 'md-right'){
37754             pos.push({
37755                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37756                 y : minY + (this.unitWidth + this.gutter) * 1
37757             });
37758             
37759             return pos;
37760         }
37761         
37762         var rand = Math.floor(Math.random() * (4 - box[0].y));
37763         
37764         pos.push({
37765             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37766             y : minY + (this.unitWidth + this.gutter) * rand
37767         });
37768         
37769         return pos;
37770         
37771     },
37772     
37773     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
37774     {
37775         var pos = [];
37776         
37777         if(box[0].size == 'xs'){
37778             
37779             pos.push({
37780                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37781                 y : minY
37782             });
37783
37784             pos.push({
37785                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37786                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
37787             });
37788             
37789             return pos;
37790             
37791         }
37792         
37793         pos.push({
37794             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37795             y : minY
37796         });
37797
37798         pos.push({
37799             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37800             y : minY + (this.unitWidth + this.gutter) * 2
37801         });
37802         
37803         return pos;
37804         
37805     },
37806     
37807     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
37808     {
37809         var pos = [];
37810         
37811         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
37812             
37813             pos.push({
37814                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37815                 y : minY
37816             });
37817
37818             pos.push({
37819                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37820                 y : minY + (this.unitWidth + this.gutter) * 1
37821             });
37822             
37823             pos.push({
37824                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37825                 y : minY + (this.unitWidth + this.gutter) * 2
37826             });
37827             
37828             return pos;
37829             
37830         }
37831         
37832         if(box[0].size == 'xs' && box[1].size == 'xs'){
37833             
37834             pos.push({
37835                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37836                 y : minY
37837             });
37838
37839             pos.push({
37840                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37841                 y : minY
37842             });
37843             
37844             pos.push({
37845                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37846                 y : minY + (this.unitWidth + this.gutter) * 1
37847             });
37848             
37849             return pos;
37850             
37851         }
37852         
37853         pos.push({
37854             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37855             y : minY
37856         });
37857
37858         pos.push({
37859             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37860             y : minY + (this.unitWidth + this.gutter) * 2
37861         });
37862
37863         pos.push({
37864             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37865             y : minY + (this.unitWidth + this.gutter) * 2
37866         });
37867             
37868         return pos;
37869         
37870     },
37871     
37872     getHorizontalFourBoxColPositions : function(maxX, minY, box)
37873     {
37874         var pos = [];
37875         
37876         if(box[0].size == 'xs'){
37877             
37878             pos.push({
37879                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37880                 y : minY
37881             });
37882
37883             pos.push({
37884                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37885                 y : minY
37886             });
37887             
37888             pos.push({
37889                 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),
37890                 y : minY
37891             });
37892             
37893             pos.push({
37894                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
37895                 y : minY + (this.unitWidth + this.gutter) * 1
37896             });
37897             
37898             return pos;
37899             
37900         }
37901         
37902         pos.push({
37903             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37904             y : minY
37905         });
37906         
37907         pos.push({
37908             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37909             y : minY + (this.unitWidth + this.gutter) * 2
37910         });
37911         
37912         pos.push({
37913             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37914             y : minY + (this.unitWidth + this.gutter) * 2
37915         });
37916         
37917         pos.push({
37918             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),
37919             y : minY + (this.unitWidth + this.gutter) * 2
37920         });
37921
37922         return pos;
37923         
37924     },
37925     
37926     /**
37927     * remove a Masonry Brick
37928     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
37929     */
37930     removeBrick : function(brick_id)
37931     {
37932         if (!brick_id) {
37933             return;
37934         }
37935         
37936         for (var i = 0; i<this.bricks.length; i++) {
37937             if (this.bricks[i].id == brick_id) {
37938                 this.bricks.splice(i,1);
37939                 this.el.dom.removeChild(Roo.get(brick_id).dom);
37940                 this.initial();
37941             }
37942         }
37943     },
37944     
37945     /**
37946     * adds a Masonry Brick
37947     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
37948     */
37949     addBrick : function(cfg)
37950     {
37951         var cn = new Roo.bootstrap.MasonryBrick(cfg);
37952         //this.register(cn);
37953         cn.parentId = this.id;
37954         cn.render(this.el);
37955         return cn;
37956     },
37957     
37958     /**
37959     * register a Masonry Brick
37960     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
37961     */
37962     
37963     register : function(brick)
37964     {
37965         this.bricks.push(brick);
37966         brick.masonryId = this.id;
37967     },
37968     
37969     /**
37970     * clear all the Masonry Brick
37971     */
37972     clearAll : function()
37973     {
37974         this.bricks = [];
37975         //this.getChildContainer().dom.innerHTML = "";
37976         this.el.dom.innerHTML = '';
37977     },
37978     
37979     getSelected : function()
37980     {
37981         if (!this.selectedBrick) {
37982             return false;
37983         }
37984         
37985         return this.selectedBrick;
37986     }
37987 });
37988
37989 Roo.apply(Roo.bootstrap.LayoutMasonry, {
37990     
37991     groups: {},
37992      /**
37993     * register a Masonry Layout
37994     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
37995     */
37996     
37997     register : function(layout)
37998     {
37999         this.groups[layout.id] = layout;
38000     },
38001     /**
38002     * fetch a  Masonry Layout based on the masonry layout ID
38003     * @param {string} the masonry layout to add
38004     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
38005     */
38006     
38007     get: function(layout_id) {
38008         if (typeof(this.groups[layout_id]) == 'undefined') {
38009             return false;
38010         }
38011         return this.groups[layout_id] ;
38012     }
38013     
38014     
38015     
38016 });
38017
38018  
38019
38020  /**
38021  *
38022  * This is based on 
38023  * http://masonry.desandro.com
38024  *
38025  * The idea is to render all the bricks based on vertical width...
38026  *
38027  * The original code extends 'outlayer' - we might need to use that....
38028  * 
38029  */
38030
38031
38032 /**
38033  * @class Roo.bootstrap.LayoutMasonryAuto
38034  * @extends Roo.bootstrap.Component
38035  * Bootstrap Layout Masonry class
38036  * 
38037  * @constructor
38038  * Create a new Element
38039  * @param {Object} config The config object
38040  */
38041
38042 Roo.bootstrap.LayoutMasonryAuto = function(config){
38043     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
38044 };
38045
38046 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
38047     
38048       /**
38049      * @cfg {Boolean} isFitWidth  - resize the width..
38050      */   
38051     isFitWidth : false,  // options..
38052     /**
38053      * @cfg {Boolean} isOriginLeft = left align?
38054      */   
38055     isOriginLeft : true,
38056     /**
38057      * @cfg {Boolean} isOriginTop = top align?
38058      */   
38059     isOriginTop : false,
38060     /**
38061      * @cfg {Boolean} isLayoutInstant = no animation?
38062      */   
38063     isLayoutInstant : false, // needed?
38064     /**
38065      * @cfg {Boolean} isResizingContainer = not sure if this is used..
38066      */   
38067     isResizingContainer : true,
38068     /**
38069      * @cfg {Number} columnWidth  width of the columns 
38070      */   
38071     
38072     columnWidth : 0,
38073     
38074     /**
38075      * @cfg {Number} maxCols maximum number of columns
38076      */   
38077     
38078     maxCols: 0,
38079     /**
38080      * @cfg {Number} padHeight padding below box..
38081      */   
38082     
38083     padHeight : 10, 
38084     
38085     /**
38086      * @cfg {Boolean} isAutoInitial defalut true
38087      */   
38088     
38089     isAutoInitial : true, 
38090     
38091     // private?
38092     gutter : 0,
38093     
38094     containerWidth: 0,
38095     initialColumnWidth : 0,
38096     currentSize : null,
38097     
38098     colYs : null, // array.
38099     maxY : 0,
38100     padWidth: 10,
38101     
38102     
38103     tag: 'div',
38104     cls: '',
38105     bricks: null, //CompositeElement
38106     cols : 0, // array?
38107     // element : null, // wrapped now this.el
38108     _isLayoutInited : null, 
38109     
38110     
38111     getAutoCreate : function(){
38112         
38113         var cfg = {
38114             tag: this.tag,
38115             cls: 'blog-masonary-wrapper ' + this.cls,
38116             cn : {
38117                 cls : 'mas-boxes masonary'
38118             }
38119         };
38120         
38121         return cfg;
38122     },
38123     
38124     getChildContainer: function( )
38125     {
38126         if (this.boxesEl) {
38127             return this.boxesEl;
38128         }
38129         
38130         this.boxesEl = this.el.select('.mas-boxes').first();
38131         
38132         return this.boxesEl;
38133     },
38134     
38135     
38136     initEvents : function()
38137     {
38138         var _this = this;
38139         
38140         if(this.isAutoInitial){
38141             Roo.log('hook children rendered');
38142             this.on('childrenrendered', function() {
38143                 Roo.log('children rendered');
38144                 _this.initial();
38145             } ,this);
38146         }
38147         
38148     },
38149     
38150     initial : function()
38151     {
38152         this.reloadItems();
38153
38154         this.currentSize = this.el.getBox(true);
38155
38156         /// was window resize... - let's see if this works..
38157         Roo.EventManager.onWindowResize(this.resize, this); 
38158
38159         if(!this.isAutoInitial){
38160             this.layout();
38161             return;
38162         }
38163         
38164         this.layout.defer(500,this);
38165     },
38166     
38167     reloadItems: function()
38168     {
38169         this.bricks = this.el.select('.masonry-brick', true);
38170         
38171         this.bricks.each(function(b) {
38172             //Roo.log(b.getSize());
38173             if (!b.attr('originalwidth')) {
38174                 b.attr('originalwidth',  b.getSize().width);
38175             }
38176             
38177         });
38178         
38179         Roo.log(this.bricks.elements.length);
38180     },
38181     
38182     resize : function()
38183     {
38184         Roo.log('resize');
38185         var cs = this.el.getBox(true);
38186         
38187         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
38188             Roo.log("no change in with or X");
38189             return;
38190         }
38191         this.currentSize = cs;
38192         this.layout();
38193     },
38194     
38195     layout : function()
38196     {
38197          Roo.log('layout');
38198         this._resetLayout();
38199         //this._manageStamps();
38200       
38201         // don't animate first layout
38202         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38203         this.layoutItems( isInstant );
38204       
38205         // flag for initalized
38206         this._isLayoutInited = true;
38207     },
38208     
38209     layoutItems : function( isInstant )
38210     {
38211         //var items = this._getItemsForLayout( this.items );
38212         // original code supports filtering layout items.. we just ignore it..
38213         
38214         this._layoutItems( this.bricks , isInstant );
38215       
38216         this._postLayout();
38217     },
38218     _layoutItems : function ( items , isInstant)
38219     {
38220        //this.fireEvent( 'layout', this, items );
38221     
38222
38223         if ( !items || !items.elements.length ) {
38224           // no items, emit event with empty array
38225             return;
38226         }
38227
38228         var queue = [];
38229         items.each(function(item) {
38230             Roo.log("layout item");
38231             Roo.log(item);
38232             // get x/y object from method
38233             var position = this._getItemLayoutPosition( item );
38234             // enqueue
38235             position.item = item;
38236             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
38237             queue.push( position );
38238         }, this);
38239       
38240         this._processLayoutQueue( queue );
38241     },
38242     /** Sets position of item in DOM
38243     * @param {Element} item
38244     * @param {Number} x - horizontal position
38245     * @param {Number} y - vertical position
38246     * @param {Boolean} isInstant - disables transitions
38247     */
38248     _processLayoutQueue : function( queue )
38249     {
38250         for ( var i=0, len = queue.length; i < len; i++ ) {
38251             var obj = queue[i];
38252             obj.item.position('absolute');
38253             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
38254         }
38255     },
38256       
38257     
38258     /**
38259     * Any logic you want to do after each layout,
38260     * i.e. size the container
38261     */
38262     _postLayout : function()
38263     {
38264         this.resizeContainer();
38265     },
38266     
38267     resizeContainer : function()
38268     {
38269         if ( !this.isResizingContainer ) {
38270             return;
38271         }
38272         var size = this._getContainerSize();
38273         if ( size ) {
38274             this.el.setSize(size.width,size.height);
38275             this.boxesEl.setSize(size.width,size.height);
38276         }
38277     },
38278     
38279     
38280     
38281     _resetLayout : function()
38282     {
38283         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
38284         this.colWidth = this.el.getWidth();
38285         //this.gutter = this.el.getWidth(); 
38286         
38287         this.measureColumns();
38288
38289         // reset column Y
38290         var i = this.cols;
38291         this.colYs = [];
38292         while (i--) {
38293             this.colYs.push( 0 );
38294         }
38295     
38296         this.maxY = 0;
38297     },
38298
38299     measureColumns : function()
38300     {
38301         this.getContainerWidth();
38302       // if columnWidth is 0, default to outerWidth of first item
38303         if ( !this.columnWidth ) {
38304             var firstItem = this.bricks.first();
38305             Roo.log(firstItem);
38306             this.columnWidth  = this.containerWidth;
38307             if (firstItem && firstItem.attr('originalwidth') ) {
38308                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
38309             }
38310             // columnWidth fall back to item of first element
38311             Roo.log("set column width?");
38312                         this.initialColumnWidth = this.columnWidth  ;
38313
38314             // if first elem has no width, default to size of container
38315             
38316         }
38317         
38318         
38319         if (this.initialColumnWidth) {
38320             this.columnWidth = this.initialColumnWidth;
38321         }
38322         
38323         
38324             
38325         // column width is fixed at the top - however if container width get's smaller we should
38326         // reduce it...
38327         
38328         // this bit calcs how man columns..
38329             
38330         var columnWidth = this.columnWidth += this.gutter;
38331       
38332         // calculate columns
38333         var containerWidth = this.containerWidth + this.gutter;
38334         
38335         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
38336         // fix rounding errors, typically with gutters
38337         var excess = columnWidth - containerWidth % columnWidth;
38338         
38339         
38340         // if overshoot is less than a pixel, round up, otherwise floor it
38341         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
38342         cols = Math[ mathMethod ]( cols );
38343         this.cols = Math.max( cols, 1 );
38344         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38345         
38346          // padding positioning..
38347         var totalColWidth = this.cols * this.columnWidth;
38348         var padavail = this.containerWidth - totalColWidth;
38349         // so for 2 columns - we need 3 'pads'
38350         
38351         var padNeeded = (1+this.cols) * this.padWidth;
38352         
38353         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
38354         
38355         this.columnWidth += padExtra
38356         //this.padWidth = Math.floor(padavail /  ( this.cols));
38357         
38358         // adjust colum width so that padding is fixed??
38359         
38360         // we have 3 columns ... total = width * 3
38361         // we have X left over... that should be used by 
38362         
38363         //if (this.expandC) {
38364             
38365         //}
38366         
38367         
38368         
38369     },
38370     
38371     getContainerWidth : function()
38372     {
38373        /* // container is parent if fit width
38374         var container = this.isFitWidth ? this.element.parentNode : this.element;
38375         // check that this.size and size are there
38376         // IE8 triggers resize on body size change, so they might not be
38377         
38378         var size = getSize( container );  //FIXME
38379         this.containerWidth = size && size.innerWidth; //FIXME
38380         */
38381          
38382         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
38383         
38384     },
38385     
38386     _getItemLayoutPosition : function( item )  // what is item?
38387     {
38388         // we resize the item to our columnWidth..
38389       
38390         item.setWidth(this.columnWidth);
38391         item.autoBoxAdjust  = false;
38392         
38393         var sz = item.getSize();
38394  
38395         // how many columns does this brick span
38396         var remainder = this.containerWidth % this.columnWidth;
38397         
38398         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
38399         // round if off by 1 pixel, otherwise use ceil
38400         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
38401         colSpan = Math.min( colSpan, this.cols );
38402         
38403         // normally this should be '1' as we dont' currently allow multi width columns..
38404         
38405         var colGroup = this._getColGroup( colSpan );
38406         // get the minimum Y value from the columns
38407         var minimumY = Math.min.apply( Math, colGroup );
38408         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
38409         
38410         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
38411          
38412         // position the brick
38413         var position = {
38414             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
38415             y: this.currentSize.y + minimumY + this.padHeight
38416         };
38417         
38418         Roo.log(position);
38419         // apply setHeight to necessary columns
38420         var setHeight = minimumY + sz.height + this.padHeight;
38421         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
38422         
38423         var setSpan = this.cols + 1 - colGroup.length;
38424         for ( var i = 0; i < setSpan; i++ ) {
38425           this.colYs[ shortColIndex + i ] = setHeight ;
38426         }
38427       
38428         return position;
38429     },
38430     
38431     /**
38432      * @param {Number} colSpan - number of columns the element spans
38433      * @returns {Array} colGroup
38434      */
38435     _getColGroup : function( colSpan )
38436     {
38437         if ( colSpan < 2 ) {
38438           // if brick spans only one column, use all the column Ys
38439           return this.colYs;
38440         }
38441       
38442         var colGroup = [];
38443         // how many different places could this brick fit horizontally
38444         var groupCount = this.cols + 1 - colSpan;
38445         // for each group potential horizontal position
38446         for ( var i = 0; i < groupCount; i++ ) {
38447           // make an array of colY values for that one group
38448           var groupColYs = this.colYs.slice( i, i + colSpan );
38449           // and get the max value of the array
38450           colGroup[i] = Math.max.apply( Math, groupColYs );
38451         }
38452         return colGroup;
38453     },
38454     /*
38455     _manageStamp : function( stamp )
38456     {
38457         var stampSize =  stamp.getSize();
38458         var offset = stamp.getBox();
38459         // get the columns that this stamp affects
38460         var firstX = this.isOriginLeft ? offset.x : offset.right;
38461         var lastX = firstX + stampSize.width;
38462         var firstCol = Math.floor( firstX / this.columnWidth );
38463         firstCol = Math.max( 0, firstCol );
38464         
38465         var lastCol = Math.floor( lastX / this.columnWidth );
38466         // lastCol should not go over if multiple of columnWidth #425
38467         lastCol -= lastX % this.columnWidth ? 0 : 1;
38468         lastCol = Math.min( this.cols - 1, lastCol );
38469         
38470         // set colYs to bottom of the stamp
38471         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
38472             stampSize.height;
38473             
38474         for ( var i = firstCol; i <= lastCol; i++ ) {
38475           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
38476         }
38477     },
38478     */
38479     
38480     _getContainerSize : function()
38481     {
38482         this.maxY = Math.max.apply( Math, this.colYs );
38483         var size = {
38484             height: this.maxY
38485         };
38486       
38487         if ( this.isFitWidth ) {
38488             size.width = this._getContainerFitWidth();
38489         }
38490       
38491         return size;
38492     },
38493     
38494     _getContainerFitWidth : function()
38495     {
38496         var unusedCols = 0;
38497         // count unused columns
38498         var i = this.cols;
38499         while ( --i ) {
38500           if ( this.colYs[i] !== 0 ) {
38501             break;
38502           }
38503           unusedCols++;
38504         }
38505         // fit container to columns that have been used
38506         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
38507     },
38508     
38509     needsResizeLayout : function()
38510     {
38511         var previousWidth = this.containerWidth;
38512         this.getContainerWidth();
38513         return previousWidth !== this.containerWidth;
38514     }
38515  
38516 });
38517
38518  
38519
38520  /*
38521  * - LGPL
38522  *
38523  * element
38524  * 
38525  */
38526
38527 /**
38528  * @class Roo.bootstrap.MasonryBrick
38529  * @extends Roo.bootstrap.Component
38530  * Bootstrap MasonryBrick class
38531  * 
38532  * @constructor
38533  * Create a new MasonryBrick
38534  * @param {Object} config The config object
38535  */
38536
38537 Roo.bootstrap.MasonryBrick = function(config){
38538     
38539     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
38540     
38541     Roo.bootstrap.MasonryBrick.register(this);
38542     
38543     this.addEvents({
38544         // raw events
38545         /**
38546          * @event click
38547          * When a MasonryBrick is clcik
38548          * @param {Roo.bootstrap.MasonryBrick} this
38549          * @param {Roo.EventObject} e
38550          */
38551         "click" : true
38552     });
38553 };
38554
38555 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
38556     
38557     /**
38558      * @cfg {String} title
38559      */   
38560     title : '',
38561     /**
38562      * @cfg {String} html
38563      */   
38564     html : '',
38565     /**
38566      * @cfg {String} bgimage
38567      */   
38568     bgimage : '',
38569     /**
38570      * @cfg {String} videourl
38571      */   
38572     videourl : '',
38573     /**
38574      * @cfg {String} cls
38575      */   
38576     cls : '',
38577     /**
38578      * @cfg {String} href
38579      */   
38580     href : '',
38581     /**
38582      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
38583      */   
38584     size : 'xs',
38585     
38586     /**
38587      * @cfg {String} placetitle (center|bottom)
38588      */   
38589     placetitle : '',
38590     
38591     /**
38592      * @cfg {Boolean} isFitContainer defalut true
38593      */   
38594     isFitContainer : true, 
38595     
38596     /**
38597      * @cfg {Boolean} preventDefault defalut false
38598      */   
38599     preventDefault : false, 
38600     
38601     /**
38602      * @cfg {Boolean} inverse defalut false
38603      */   
38604     maskInverse : false, 
38605     
38606     getAutoCreate : function()
38607     {
38608         if(!this.isFitContainer){
38609             return this.getSplitAutoCreate();
38610         }
38611         
38612         var cls = 'masonry-brick masonry-brick-full';
38613         
38614         if(this.href.length){
38615             cls += ' masonry-brick-link';
38616         }
38617         
38618         if(this.bgimage.length){
38619             cls += ' masonry-brick-image';
38620         }
38621         
38622         if(this.maskInverse){
38623             cls += ' mask-inverse';
38624         }
38625         
38626         if(!this.html.length && !this.maskInverse && !this.videourl.length){
38627             cls += ' enable-mask';
38628         }
38629         
38630         if(this.size){
38631             cls += ' masonry-' + this.size + '-brick';
38632         }
38633         
38634         if(this.placetitle.length){
38635             
38636             switch (this.placetitle) {
38637                 case 'center' :
38638                     cls += ' masonry-center-title';
38639                     break;
38640                 case 'bottom' :
38641                     cls += ' masonry-bottom-title';
38642                     break;
38643                 default:
38644                     break;
38645             }
38646             
38647         } else {
38648             if(!this.html.length && !this.bgimage.length){
38649                 cls += ' masonry-center-title';
38650             }
38651
38652             if(!this.html.length && this.bgimage.length){
38653                 cls += ' masonry-bottom-title';
38654             }
38655         }
38656         
38657         if(this.cls){
38658             cls += ' ' + this.cls;
38659         }
38660         
38661         var cfg = {
38662             tag: (this.href.length) ? 'a' : 'div',
38663             cls: cls,
38664             cn: [
38665                 {
38666                     tag: 'div',
38667                     cls: 'masonry-brick-mask'
38668                 },
38669                 {
38670                     tag: 'div',
38671                     cls: 'masonry-brick-paragraph',
38672                     cn: []
38673                 }
38674             ]
38675         };
38676         
38677         if(this.href.length){
38678             cfg.href = this.href;
38679         }
38680         
38681         var cn = cfg.cn[1].cn;
38682         
38683         if(this.title.length){
38684             cn.push({
38685                 tag: 'h4',
38686                 cls: 'masonry-brick-title',
38687                 html: this.title
38688             });
38689         }
38690         
38691         if(this.html.length){
38692             cn.push({
38693                 tag: 'p',
38694                 cls: 'masonry-brick-text',
38695                 html: this.html
38696             });
38697         }
38698         
38699         if (!this.title.length && !this.html.length) {
38700             cfg.cn[1].cls += ' hide';
38701         }
38702         
38703         if(this.bgimage.length){
38704             cfg.cn.push({
38705                 tag: 'img',
38706                 cls: 'masonry-brick-image-view',
38707                 src: this.bgimage
38708             });
38709         }
38710         
38711         if(this.videourl.length){
38712             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
38713             // youtube support only?
38714             cfg.cn.push({
38715                 tag: 'iframe',
38716                 cls: 'masonry-brick-image-view',
38717                 src: vurl,
38718                 frameborder : 0,
38719                 allowfullscreen : true
38720             });
38721         }
38722         
38723         return cfg;
38724         
38725     },
38726     
38727     getSplitAutoCreate : function()
38728     {
38729         var cls = 'masonry-brick masonry-brick-split';
38730         
38731         if(this.href.length){
38732             cls += ' masonry-brick-link';
38733         }
38734         
38735         if(this.bgimage.length){
38736             cls += ' masonry-brick-image';
38737         }
38738         
38739         if(this.size){
38740             cls += ' masonry-' + this.size + '-brick';
38741         }
38742         
38743         switch (this.placetitle) {
38744             case 'center' :
38745                 cls += ' masonry-center-title';
38746                 break;
38747             case 'bottom' :
38748                 cls += ' masonry-bottom-title';
38749                 break;
38750             default:
38751                 if(!this.bgimage.length){
38752                     cls += ' masonry-center-title';
38753                 }
38754
38755                 if(this.bgimage.length){
38756                     cls += ' masonry-bottom-title';
38757                 }
38758                 break;
38759         }
38760         
38761         if(this.cls){
38762             cls += ' ' + this.cls;
38763         }
38764         
38765         var cfg = {
38766             tag: (this.href.length) ? 'a' : 'div',
38767             cls: cls,
38768             cn: [
38769                 {
38770                     tag: 'div',
38771                     cls: 'masonry-brick-split-head',
38772                     cn: [
38773                         {
38774                             tag: 'div',
38775                             cls: 'masonry-brick-paragraph',
38776                             cn: []
38777                         }
38778                     ]
38779                 },
38780                 {
38781                     tag: 'div',
38782                     cls: 'masonry-brick-split-body',
38783                     cn: []
38784                 }
38785             ]
38786         };
38787         
38788         if(this.href.length){
38789             cfg.href = this.href;
38790         }
38791         
38792         if(this.title.length){
38793             cfg.cn[0].cn[0].cn.push({
38794                 tag: 'h4',
38795                 cls: 'masonry-brick-title',
38796                 html: this.title
38797             });
38798         }
38799         
38800         if(this.html.length){
38801             cfg.cn[1].cn.push({
38802                 tag: 'p',
38803                 cls: 'masonry-brick-text',
38804                 html: this.html
38805             });
38806         }
38807
38808         if(this.bgimage.length){
38809             cfg.cn[0].cn.push({
38810                 tag: 'img',
38811                 cls: 'masonry-brick-image-view',
38812                 src: this.bgimage
38813             });
38814         }
38815         
38816         if(this.videourl.length){
38817             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
38818             // youtube support only?
38819             cfg.cn[0].cn.cn.push({
38820                 tag: 'iframe',
38821                 cls: 'masonry-brick-image-view',
38822                 src: vurl,
38823                 frameborder : 0,
38824                 allowfullscreen : true
38825             });
38826         }
38827         
38828         return cfg;
38829     },
38830     
38831     initEvents: function() 
38832     {
38833         switch (this.size) {
38834             case 'xs' :
38835                 this.x = 1;
38836                 this.y = 1;
38837                 break;
38838             case 'sm' :
38839                 this.x = 2;
38840                 this.y = 2;
38841                 break;
38842             case 'md' :
38843             case 'md-left' :
38844             case 'md-right' :
38845                 this.x = 3;
38846                 this.y = 3;
38847                 break;
38848             case 'tall' :
38849                 this.x = 2;
38850                 this.y = 3;
38851                 break;
38852             case 'wide' :
38853                 this.x = 3;
38854                 this.y = 2;
38855                 break;
38856             case 'wide-thin' :
38857                 this.x = 3;
38858                 this.y = 1;
38859                 break;
38860                         
38861             default :
38862                 break;
38863         }
38864         
38865         if(Roo.isTouch){
38866             this.el.on('touchstart', this.onTouchStart, this);
38867             this.el.on('touchmove', this.onTouchMove, this);
38868             this.el.on('touchend', this.onTouchEnd, this);
38869             this.el.on('contextmenu', this.onContextMenu, this);
38870         } else {
38871             this.el.on('mouseenter'  ,this.enter, this);
38872             this.el.on('mouseleave', this.leave, this);
38873             this.el.on('click', this.onClick, this);
38874         }
38875         
38876         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
38877             this.parent().bricks.push(this);   
38878         }
38879         
38880     },
38881     
38882     onClick: function(e, el)
38883     {
38884         var time = this.endTimer - this.startTimer;
38885         // Roo.log(e.preventDefault());
38886         if(Roo.isTouch){
38887             if(time > 1000){
38888                 e.preventDefault();
38889                 return;
38890             }
38891         }
38892         
38893         if(!this.preventDefault){
38894             return;
38895         }
38896         
38897         e.preventDefault();
38898         
38899         if (this.activeClass != '') {
38900             this.selectBrick();
38901         }
38902         
38903         this.fireEvent('click', this, e);
38904     },
38905     
38906     enter: function(e, el)
38907     {
38908         e.preventDefault();
38909         
38910         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
38911             return;
38912         }
38913         
38914         if(this.bgimage.length && this.html.length){
38915             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
38916         }
38917     },
38918     
38919     leave: function(e, el)
38920     {
38921         e.preventDefault();
38922         
38923         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
38924             return;
38925         }
38926         
38927         if(this.bgimage.length && this.html.length){
38928             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
38929         }
38930     },
38931     
38932     onTouchStart: function(e, el)
38933     {
38934 //        e.preventDefault();
38935         
38936         this.touchmoved = false;
38937         
38938         if(!this.isFitContainer){
38939             return;
38940         }
38941         
38942         if(!this.bgimage.length || !this.html.length){
38943             return;
38944         }
38945         
38946         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
38947         
38948         this.timer = new Date().getTime();
38949         
38950     },
38951     
38952     onTouchMove: function(e, el)
38953     {
38954         this.touchmoved = true;
38955     },
38956     
38957     onContextMenu : function(e,el)
38958     {
38959         e.preventDefault();
38960         e.stopPropagation();
38961         return false;
38962     },
38963     
38964     onTouchEnd: function(e, el)
38965     {
38966 //        e.preventDefault();
38967         
38968         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
38969         
38970             this.leave(e,el);
38971             
38972             return;
38973         }
38974         
38975         if(!this.bgimage.length || !this.html.length){
38976             
38977             if(this.href.length){
38978                 window.location.href = this.href;
38979             }
38980             
38981             return;
38982         }
38983         
38984         if(!this.isFitContainer){
38985             return;
38986         }
38987         
38988         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
38989         
38990         window.location.href = this.href;
38991     },
38992     
38993     //selection on single brick only
38994     selectBrick : function() {
38995         
38996         if (!this.parentId) {
38997             return;
38998         }
38999         
39000         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
39001         var index = m.selectedBrick.indexOf(this.id);
39002         
39003         if ( index > -1) {
39004             m.selectedBrick.splice(index,1);
39005             this.el.removeClass(this.activeClass);
39006             return;
39007         }
39008         
39009         for(var i = 0; i < m.selectedBrick.length; i++) {
39010             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
39011             b.el.removeClass(b.activeClass);
39012         }
39013         
39014         m.selectedBrick = [];
39015         
39016         m.selectedBrick.push(this.id);
39017         this.el.addClass(this.activeClass);
39018         return;
39019     },
39020     
39021     isSelected : function(){
39022         return this.el.hasClass(this.activeClass);
39023         
39024     }
39025 });
39026
39027 Roo.apply(Roo.bootstrap.MasonryBrick, {
39028     
39029     //groups: {},
39030     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
39031      /**
39032     * register a Masonry Brick
39033     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39034     */
39035     
39036     register : function(brick)
39037     {
39038         //this.groups[brick.id] = brick;
39039         this.groups.add(brick.id, brick);
39040     },
39041     /**
39042     * fetch a  masonry brick based on the masonry brick ID
39043     * @param {string} the masonry brick to add
39044     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
39045     */
39046     
39047     get: function(brick_id) 
39048     {
39049         // if (typeof(this.groups[brick_id]) == 'undefined') {
39050         //     return false;
39051         // }
39052         // return this.groups[brick_id] ;
39053         
39054         if(this.groups.key(brick_id)) {
39055             return this.groups.key(brick_id);
39056         }
39057         
39058         return false;
39059     }
39060     
39061     
39062     
39063 });
39064
39065  /*
39066  * - LGPL
39067  *
39068  * element
39069  * 
39070  */
39071
39072 /**
39073  * @class Roo.bootstrap.Brick
39074  * @extends Roo.bootstrap.Component
39075  * Bootstrap Brick class
39076  * 
39077  * @constructor
39078  * Create a new Brick
39079  * @param {Object} config The config object
39080  */
39081
39082 Roo.bootstrap.Brick = function(config){
39083     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
39084     
39085     this.addEvents({
39086         // raw events
39087         /**
39088          * @event click
39089          * When a Brick is click
39090          * @param {Roo.bootstrap.Brick} this
39091          * @param {Roo.EventObject} e
39092          */
39093         "click" : true
39094     });
39095 };
39096
39097 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
39098     
39099     /**
39100      * @cfg {String} title
39101      */   
39102     title : '',
39103     /**
39104      * @cfg {String} html
39105      */   
39106     html : '',
39107     /**
39108      * @cfg {String} bgimage
39109      */   
39110     bgimage : '',
39111     /**
39112      * @cfg {String} cls
39113      */   
39114     cls : '',
39115     /**
39116      * @cfg {String} href
39117      */   
39118     href : '',
39119     /**
39120      * @cfg {String} video
39121      */   
39122     video : '',
39123     /**
39124      * @cfg {Boolean} square
39125      */   
39126     square : true,
39127     
39128     getAutoCreate : function()
39129     {
39130         var cls = 'roo-brick';
39131         
39132         if(this.href.length){
39133             cls += ' roo-brick-link';
39134         }
39135         
39136         if(this.bgimage.length){
39137             cls += ' roo-brick-image';
39138         }
39139         
39140         if(!this.html.length && !this.bgimage.length){
39141             cls += ' roo-brick-center-title';
39142         }
39143         
39144         if(!this.html.length && this.bgimage.length){
39145             cls += ' roo-brick-bottom-title';
39146         }
39147         
39148         if(this.cls){
39149             cls += ' ' + this.cls;
39150         }
39151         
39152         var cfg = {
39153             tag: (this.href.length) ? 'a' : 'div',
39154             cls: cls,
39155             cn: [
39156                 {
39157                     tag: 'div',
39158                     cls: 'roo-brick-paragraph',
39159                     cn: []
39160                 }
39161             ]
39162         };
39163         
39164         if(this.href.length){
39165             cfg.href = this.href;
39166         }
39167         
39168         var cn = cfg.cn[0].cn;
39169         
39170         if(this.title.length){
39171             cn.push({
39172                 tag: 'h4',
39173                 cls: 'roo-brick-title',
39174                 html: this.title
39175             });
39176         }
39177         
39178         if(this.html.length){
39179             cn.push({
39180                 tag: 'p',
39181                 cls: 'roo-brick-text',
39182                 html: this.html
39183             });
39184         } else {
39185             cn.cls += ' hide';
39186         }
39187         
39188         if(this.bgimage.length){
39189             cfg.cn.push({
39190                 tag: 'img',
39191                 cls: 'roo-brick-image-view',
39192                 src: this.bgimage
39193             });
39194         }
39195         
39196         return cfg;
39197     },
39198     
39199     initEvents: function() 
39200     {
39201         if(this.title.length || this.html.length){
39202             this.el.on('mouseenter'  ,this.enter, this);
39203             this.el.on('mouseleave', this.leave, this);
39204         }
39205         
39206         Roo.EventManager.onWindowResize(this.resize, this); 
39207         
39208         if(this.bgimage.length){
39209             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
39210             this.imageEl.on('load', this.onImageLoad, this);
39211             return;
39212         }
39213         
39214         this.resize();
39215     },
39216     
39217     onImageLoad : function()
39218     {
39219         this.resize();
39220     },
39221     
39222     resize : function()
39223     {
39224         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
39225         
39226         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
39227         
39228         if(this.bgimage.length){
39229             var image = this.el.select('.roo-brick-image-view', true).first();
39230             
39231             image.setWidth(paragraph.getWidth());
39232             
39233             if(this.square){
39234                 image.setHeight(paragraph.getWidth());
39235             }
39236             
39237             this.el.setHeight(image.getHeight());
39238             paragraph.setHeight(image.getHeight());
39239             
39240         }
39241         
39242     },
39243     
39244     enter: function(e, el)
39245     {
39246         e.preventDefault();
39247         
39248         if(this.bgimage.length){
39249             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
39250             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
39251         }
39252     },
39253     
39254     leave: function(e, el)
39255     {
39256         e.preventDefault();
39257         
39258         if(this.bgimage.length){
39259             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
39260             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
39261         }
39262     }
39263     
39264 });
39265
39266  
39267
39268  /*
39269  * - LGPL
39270  *
39271  * Number field 
39272  */
39273
39274 /**
39275  * @class Roo.bootstrap.form.NumberField
39276  * @extends Roo.bootstrap.form.Input
39277  * Bootstrap NumberField class
39278  * 
39279  * 
39280  * 
39281  * 
39282  * @constructor
39283  * Create a new NumberField
39284  * @param {Object} config The config object
39285  */
39286
39287 Roo.bootstrap.form.NumberField = function(config){
39288     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
39289 };
39290
39291 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
39292     
39293     /**
39294      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39295      */
39296     allowDecimals : true,
39297     /**
39298      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39299      */
39300     decimalSeparator : ".",
39301     /**
39302      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39303      */
39304     decimalPrecision : 2,
39305     /**
39306      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39307      */
39308     allowNegative : true,
39309     
39310     /**
39311      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
39312      */
39313     allowZero: true,
39314     /**
39315      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39316      */
39317     minValue : Number.NEGATIVE_INFINITY,
39318     /**
39319      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39320      */
39321     maxValue : Number.MAX_VALUE,
39322     /**
39323      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39324      */
39325     minText : "The minimum value for this field is {0}",
39326     /**
39327      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39328      */
39329     maxText : "The maximum value for this field is {0}",
39330     /**
39331      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39332      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39333      */
39334     nanText : "{0} is not a valid number",
39335     /**
39336      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
39337      */
39338     thousandsDelimiter : false,
39339     /**
39340      * @cfg {String} valueAlign alignment of value
39341      */
39342     valueAlign : "left",
39343
39344     getAutoCreate : function()
39345     {
39346         var hiddenInput = {
39347             tag: 'input',
39348             type: 'hidden',
39349             id: Roo.id(),
39350             cls: 'hidden-number-input'
39351         };
39352         
39353         if (this.name) {
39354             hiddenInput.name = this.name;
39355         }
39356         
39357         this.name = '';
39358         
39359         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
39360         
39361         this.name = hiddenInput.name;
39362         
39363         if(cfg.cn.length > 0) {
39364             cfg.cn.push(hiddenInput);
39365         }
39366         
39367         return cfg;
39368     },
39369
39370     // private
39371     initEvents : function()
39372     {   
39373         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
39374         
39375         var allowed = "0123456789";
39376         
39377         if(this.allowDecimals){
39378             allowed += this.decimalSeparator;
39379         }
39380         
39381         if(this.allowNegative){
39382             allowed += "-";
39383         }
39384         
39385         if(this.thousandsDelimiter) {
39386             allowed += ",";
39387         }
39388         
39389         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39390         
39391         var keyPress = function(e){
39392             
39393             var k = e.getKey();
39394             
39395             var c = e.getCharCode();
39396             
39397             if(
39398                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39399                     allowed.indexOf(String.fromCharCode(c)) === -1
39400             ){
39401                 e.stopEvent();
39402                 return;
39403             }
39404             
39405             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39406                 return;
39407             }
39408             
39409             if(allowed.indexOf(String.fromCharCode(c)) === -1){
39410                 e.stopEvent();
39411             }
39412         };
39413         
39414         this.el.on("keypress", keyPress, this);
39415     },
39416     
39417     validateValue : function(value)
39418     {
39419         
39420         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
39421             return false;
39422         }
39423         
39424         var num = this.parseValue(value);
39425         
39426         if(isNaN(num)){
39427             this.markInvalid(String.format(this.nanText, value));
39428             return false;
39429         }
39430         
39431         if(num < this.minValue){
39432             this.markInvalid(String.format(this.minText, this.minValue));
39433             return false;
39434         }
39435         
39436         if(num > this.maxValue){
39437             this.markInvalid(String.format(this.maxText, this.maxValue));
39438             return false;
39439         }
39440         
39441         return true;
39442     },
39443
39444     getValue : function()
39445     {
39446         var v = this.hiddenEl().getValue();
39447         
39448         return this.fixPrecision(this.parseValue(v));
39449     },
39450
39451     parseValue : function(value)
39452     {
39453         if(this.thousandsDelimiter) {
39454             value += "";
39455             r = new RegExp(",", "g");
39456             value = value.replace(r, "");
39457         }
39458         
39459         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
39460         return isNaN(value) ? '' : value;
39461     },
39462
39463     fixPrecision : function(value)
39464     {
39465         if(this.thousandsDelimiter) {
39466             value += "";
39467             r = new RegExp(",", "g");
39468             value = value.replace(r, "");
39469         }
39470         
39471         var nan = isNaN(value);
39472         
39473         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
39474             return nan ? '' : value;
39475         }
39476         return parseFloat(value).toFixed(this.decimalPrecision);
39477     },
39478
39479     setValue : function(v)
39480     {
39481         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
39482         
39483         this.value = v;
39484         
39485         if(this.rendered){
39486             
39487             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39488             
39489             this.inputEl().dom.value = (v == '') ? '' :
39490                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
39491             
39492             if(!this.allowZero && v === '0') {
39493                 this.hiddenEl().dom.value = '';
39494                 this.inputEl().dom.value = '';
39495             }
39496             
39497             this.validate();
39498         }
39499     },
39500
39501     decimalPrecisionFcn : function(v)
39502     {
39503         return Math.floor(v);
39504     },
39505
39506     beforeBlur : function()
39507     {
39508         var v = this.parseValue(this.getRawValue());
39509         
39510         if(v || v === 0 || v === ''){
39511             this.setValue(v);
39512         }
39513     },
39514     
39515     hiddenEl : function()
39516     {
39517         return this.el.select('input.hidden-number-input',true).first();
39518     }
39519     
39520 });
39521
39522  
39523
39524 /*
39525 * Licence: LGPL
39526 */
39527
39528 /**
39529  * @class Roo.bootstrap.DocumentSlider
39530  * @extends Roo.bootstrap.Component
39531  * Bootstrap DocumentSlider class
39532  * 
39533  * @constructor
39534  * Create a new DocumentViewer
39535  * @param {Object} config The config object
39536  */
39537
39538 Roo.bootstrap.DocumentSlider = function(config){
39539     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
39540     
39541     this.files = [];
39542     
39543     this.addEvents({
39544         /**
39545          * @event initial
39546          * Fire after initEvent
39547          * @param {Roo.bootstrap.DocumentSlider} this
39548          */
39549         "initial" : true,
39550         /**
39551          * @event update
39552          * Fire after update
39553          * @param {Roo.bootstrap.DocumentSlider} this
39554          */
39555         "update" : true,
39556         /**
39557          * @event click
39558          * Fire after click
39559          * @param {Roo.bootstrap.DocumentSlider} this
39560          */
39561         "click" : true
39562     });
39563 };
39564
39565 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
39566     
39567     files : false,
39568     
39569     indicator : 0,
39570     
39571     getAutoCreate : function()
39572     {
39573         var cfg = {
39574             tag : 'div',
39575             cls : 'roo-document-slider',
39576             cn : [
39577                 {
39578                     tag : 'div',
39579                     cls : 'roo-document-slider-header',
39580                     cn : [
39581                         {
39582                             tag : 'div',
39583                             cls : 'roo-document-slider-header-title'
39584                         }
39585                     ]
39586                 },
39587                 {
39588                     tag : 'div',
39589                     cls : 'roo-document-slider-body',
39590                     cn : [
39591                         {
39592                             tag : 'div',
39593                             cls : 'roo-document-slider-prev',
39594                             cn : [
39595                                 {
39596                                     tag : 'i',
39597                                     cls : 'fa fa-chevron-left'
39598                                 }
39599                             ]
39600                         },
39601                         {
39602                             tag : 'div',
39603                             cls : 'roo-document-slider-thumb',
39604                             cn : [
39605                                 {
39606                                     tag : 'img',
39607                                     cls : 'roo-document-slider-image'
39608                                 }
39609                             ]
39610                         },
39611                         {
39612                             tag : 'div',
39613                             cls : 'roo-document-slider-next',
39614                             cn : [
39615                                 {
39616                                     tag : 'i',
39617                                     cls : 'fa fa-chevron-right'
39618                                 }
39619                             ]
39620                         }
39621                     ]
39622                 }
39623             ]
39624         };
39625         
39626         return cfg;
39627     },
39628     
39629     initEvents : function()
39630     {
39631         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
39632         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
39633         
39634         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
39635         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
39636         
39637         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
39638         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
39639         
39640         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
39641         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
39642         
39643         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
39644         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
39645         
39646         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
39647         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39648         
39649         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
39650         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39651         
39652         this.thumbEl.on('click', this.onClick, this);
39653         
39654         this.prevIndicator.on('click', this.prev, this);
39655         
39656         this.nextIndicator.on('click', this.next, this);
39657         
39658     },
39659     
39660     initial : function()
39661     {
39662         if(this.files.length){
39663             this.indicator = 1;
39664             this.update()
39665         }
39666         
39667         this.fireEvent('initial', this);
39668     },
39669     
39670     update : function()
39671     {
39672         this.imageEl.attr('src', this.files[this.indicator - 1]);
39673         
39674         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
39675         
39676         this.prevIndicator.show();
39677         
39678         if(this.indicator == 1){
39679             this.prevIndicator.hide();
39680         }
39681         
39682         this.nextIndicator.show();
39683         
39684         if(this.indicator == this.files.length){
39685             this.nextIndicator.hide();
39686         }
39687         
39688         this.thumbEl.scrollTo('top');
39689         
39690         this.fireEvent('update', this);
39691     },
39692     
39693     onClick : function(e)
39694     {
39695         e.preventDefault();
39696         
39697         this.fireEvent('click', this);
39698     },
39699     
39700     prev : function(e)
39701     {
39702         e.preventDefault();
39703         
39704         this.indicator = Math.max(1, this.indicator - 1);
39705         
39706         this.update();
39707     },
39708     
39709     next : function(e)
39710     {
39711         e.preventDefault();
39712         
39713         this.indicator = Math.min(this.files.length, this.indicator + 1);
39714         
39715         this.update();
39716     }
39717 });
39718 /*
39719  * - LGPL
39720  *
39721  * RadioSet
39722  *
39723  *
39724  */
39725
39726 /**
39727  * @class Roo.bootstrap.form.RadioSet
39728  * @extends Roo.bootstrap.form.Input
39729  * @children Roo.bootstrap.form.Radio
39730  * Bootstrap RadioSet class
39731  * @cfg {String} indicatorpos (left|right) default left
39732  * @cfg {Boolean} inline (true|false) inline the element (default true)
39733  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
39734  * @constructor
39735  * Create a new RadioSet
39736  * @param {Object} config The config object
39737  */
39738
39739 Roo.bootstrap.form.RadioSet = function(config){
39740     
39741     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
39742     
39743     this.radioes = [];
39744     
39745     Roo.bootstrap.form.RadioSet.register(this);
39746     
39747     this.addEvents({
39748         /**
39749         * @event check
39750         * Fires when the element is checked or unchecked.
39751         * @param {Roo.bootstrap.form.RadioSet} this This radio
39752         * @param {Roo.bootstrap.form.Radio} item The checked item
39753         */
39754        check : true,
39755        /**
39756         * @event click
39757         * Fires when the element is click.
39758         * @param {Roo.bootstrap.form.RadioSet} this This radio set
39759         * @param {Roo.bootstrap.form.Radio} item The checked item
39760         * @param {Roo.EventObject} e The event object
39761         */
39762        click : true
39763     });
39764     
39765 };
39766
39767 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
39768
39769     radioes : false,
39770     
39771     inline : true,
39772     
39773     weight : '',
39774     
39775     indicatorpos : 'left',
39776     
39777     getAutoCreate : function()
39778     {
39779         var label = {
39780             tag : 'label',
39781             cls : 'roo-radio-set-label',
39782             cn : [
39783                 {
39784                     tag : 'span',
39785                     html : this.fieldLabel
39786                 }
39787             ]
39788         };
39789         if (Roo.bootstrap.version == 3) {
39790             
39791             
39792             if(this.indicatorpos == 'left'){
39793                 label.cn.unshift({
39794                     tag : 'i',
39795                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
39796                     tooltip : 'This field is required'
39797                 });
39798             } else {
39799                 label.cn.push({
39800                     tag : 'i',
39801                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
39802                     tooltip : 'This field is required'
39803                 });
39804             }
39805         }
39806         var items = {
39807             tag : 'div',
39808             cls : 'roo-radio-set-items'
39809         };
39810         
39811         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
39812         
39813         if (align === 'left' && this.fieldLabel.length) {
39814             
39815             items = {
39816                 cls : "roo-radio-set-right", 
39817                 cn: [
39818                     items
39819                 ]
39820             };
39821             
39822             if(this.labelWidth > 12){
39823                 label.style = "width: " + this.labelWidth + 'px';
39824             }
39825             
39826             if(this.labelWidth < 13 && this.labelmd == 0){
39827                 this.labelmd = this.labelWidth;
39828             }
39829             
39830             if(this.labellg > 0){
39831                 label.cls += ' col-lg-' + this.labellg;
39832                 items.cls += ' col-lg-' + (12 - this.labellg);
39833             }
39834             
39835             if(this.labelmd > 0){
39836                 label.cls += ' col-md-' + this.labelmd;
39837                 items.cls += ' col-md-' + (12 - this.labelmd);
39838             }
39839             
39840             if(this.labelsm > 0){
39841                 label.cls += ' col-sm-' + this.labelsm;
39842                 items.cls += ' col-sm-' + (12 - this.labelsm);
39843             }
39844             
39845             if(this.labelxs > 0){
39846                 label.cls += ' col-xs-' + this.labelxs;
39847                 items.cls += ' col-xs-' + (12 - this.labelxs);
39848             }
39849         }
39850         
39851         var cfg = {
39852             tag : 'div',
39853             cls : 'roo-radio-set',
39854             cn : [
39855                 {
39856                     tag : 'input',
39857                     cls : 'roo-radio-set-input',
39858                     type : 'hidden',
39859                     name : this.name,
39860                     value : this.value ? this.value :  ''
39861                 },
39862                 label,
39863                 items
39864             ]
39865         };
39866         
39867         if(this.weight.length){
39868             cfg.cls += ' roo-radio-' + this.weight;
39869         }
39870         
39871         if(this.inline) {
39872             cfg.cls += ' roo-radio-set-inline';
39873         }
39874         
39875         var settings=this;
39876         ['xs','sm','md','lg'].map(function(size){
39877             if (settings[size]) {
39878                 cfg.cls += ' col-' + size + '-' + settings[size];
39879             }
39880         });
39881         
39882         return cfg;
39883         
39884     },
39885
39886     initEvents : function()
39887     {
39888         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
39889         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
39890         
39891         if(!this.fieldLabel.length){
39892             this.labelEl.hide();
39893         }
39894         
39895         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
39896         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
39897         
39898         this.indicator = this.indicatorEl();
39899         
39900         if(this.indicator){
39901             this.indicator.addClass('invisible');
39902         }
39903         
39904         this.originalValue = this.getValue();
39905         
39906     },
39907     
39908     inputEl: function ()
39909     {
39910         return this.el.select('.roo-radio-set-input', true).first();
39911     },
39912     
39913     getChildContainer : function()
39914     {
39915         return this.itemsEl;
39916     },
39917     
39918     register : function(item)
39919     {
39920         this.radioes.push(item);
39921         
39922     },
39923     
39924     validate : function()
39925     {   
39926         if(this.getVisibilityEl().hasClass('hidden')){
39927             return true;
39928         }
39929         
39930         var valid = false;
39931         
39932         Roo.each(this.radioes, function(i){
39933             if(!i.checked){
39934                 return;
39935             }
39936             
39937             valid = true;
39938             return false;
39939         });
39940         
39941         if(this.allowBlank) {
39942             return true;
39943         }
39944         
39945         if(this.disabled || valid){
39946             this.markValid();
39947             return true;
39948         }
39949         
39950         this.markInvalid();
39951         return false;
39952         
39953     },
39954     
39955     markValid : function()
39956     {
39957         if(this.labelEl.isVisible(true) && this.indicatorEl()){
39958             this.indicatorEl().removeClass('visible');
39959             this.indicatorEl().addClass('invisible');
39960         }
39961         
39962         
39963         if (Roo.bootstrap.version == 3) {
39964             this.el.removeClass([this.invalidClass, this.validClass]);
39965             this.el.addClass(this.validClass);
39966         } else {
39967             this.el.removeClass(['is-invalid','is-valid']);
39968             this.el.addClass(['is-valid']);
39969         }
39970         this.fireEvent('valid', this);
39971     },
39972     
39973     markInvalid : function(msg)
39974     {
39975         if(this.allowBlank || this.disabled){
39976             return;
39977         }
39978         
39979         if(this.labelEl.isVisible(true) && this.indicatorEl()){
39980             this.indicatorEl().removeClass('invisible');
39981             this.indicatorEl().addClass('visible');
39982         }
39983         if (Roo.bootstrap.version == 3) {
39984             this.el.removeClass([this.invalidClass, this.validClass]);
39985             this.el.addClass(this.invalidClass);
39986         } else {
39987             this.el.removeClass(['is-invalid','is-valid']);
39988             this.el.addClass(['is-invalid']);
39989         }
39990         
39991         this.fireEvent('invalid', this, msg);
39992         
39993     },
39994     
39995     setValue : function(v, suppressEvent)
39996     {   
39997         if(this.value === v){
39998             return;
39999         }
40000         
40001         this.value = v;
40002         
40003         if(this.rendered){
40004             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40005         }
40006         
40007         Roo.each(this.radioes, function(i){
40008             i.checked = false;
40009             i.el.removeClass('checked');
40010         });
40011         
40012         Roo.each(this.radioes, function(i){
40013             
40014             if(i.value === v || i.value.toString() === v.toString()){
40015                 i.checked = true;
40016                 i.el.addClass('checked');
40017                 
40018                 if(suppressEvent !== true){
40019                     this.fireEvent('check', this, i);
40020                 }
40021                 
40022                 return false;
40023             }
40024             
40025         }, this);
40026         
40027         this.validate();
40028     },
40029     
40030     clearInvalid : function(){
40031         
40032         if(!this.el || this.preventMark){
40033             return;
40034         }
40035         
40036         this.el.removeClass([this.invalidClass]);
40037         
40038         this.fireEvent('valid', this);
40039     }
40040     
40041 });
40042
40043 Roo.apply(Roo.bootstrap.form.RadioSet, {
40044     
40045     groups: {},
40046     
40047     register : function(set)
40048     {
40049         this.groups[set.name] = set;
40050     },
40051     
40052     get: function(name) 
40053     {
40054         if (typeof(this.groups[name]) == 'undefined') {
40055             return false;
40056         }
40057         
40058         return this.groups[name] ;
40059     }
40060     
40061 });
40062 /*
40063  * Based on:
40064  * Ext JS Library 1.1.1
40065  * Copyright(c) 2006-2007, Ext JS, LLC.
40066  *
40067  * Originally Released Under LGPL - original licence link has changed is not relivant.
40068  *
40069  * Fork - LGPL
40070  * <script type="text/javascript">
40071  */
40072
40073
40074 /**
40075  * @class Roo.bootstrap.SplitBar
40076  * @extends Roo.util.Observable
40077  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
40078  * <br><br>
40079  * Usage:
40080  * <pre><code>
40081 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
40082                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
40083 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
40084 split.minSize = 100;
40085 split.maxSize = 600;
40086 split.animate = true;
40087 split.on('moved', splitterMoved);
40088 </code></pre>
40089  * @constructor
40090  * Create a new SplitBar
40091  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
40092  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
40093  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40094  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
40095                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
40096                         position of the SplitBar).
40097  */
40098 Roo.bootstrap.SplitBar = function(cfg){
40099     
40100     /** @private */
40101     
40102     //{
40103     //  dragElement : elm
40104     //  resizingElement: el,
40105         // optional..
40106     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
40107     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
40108         // existingProxy ???
40109     //}
40110     
40111     this.el = Roo.get(cfg.dragElement, true);
40112     this.el.dom.unselectable = "on";
40113     /** @private */
40114     this.resizingEl = Roo.get(cfg.resizingElement, true);
40115
40116     /**
40117      * @private
40118      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40119      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
40120      * @type Number
40121      */
40122     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
40123     
40124     /**
40125      * The minimum size of the resizing element. (Defaults to 0)
40126      * @type Number
40127      */
40128     this.minSize = 0;
40129     
40130     /**
40131      * The maximum size of the resizing element. (Defaults to 2000)
40132      * @type Number
40133      */
40134     this.maxSize = 2000;
40135     
40136     /**
40137      * Whether to animate the transition to the new size
40138      * @type Boolean
40139      */
40140     this.animate = false;
40141     
40142     /**
40143      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
40144      * @type Boolean
40145      */
40146     this.useShim = false;
40147     
40148     /** @private */
40149     this.shim = null;
40150     
40151     if(!cfg.existingProxy){
40152         /** @private */
40153         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
40154     }else{
40155         this.proxy = Roo.get(cfg.existingProxy).dom;
40156     }
40157     /** @private */
40158     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
40159     
40160     /** @private */
40161     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
40162     
40163     /** @private */
40164     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
40165     
40166     /** @private */
40167     this.dragSpecs = {};
40168     
40169     /**
40170      * @private The adapter to use to positon and resize elements
40171      */
40172     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40173     this.adapter.init(this);
40174     
40175     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40176         /** @private */
40177         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
40178         this.el.addClass("roo-splitbar-h");
40179     }else{
40180         /** @private */
40181         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
40182         this.el.addClass("roo-splitbar-v");
40183     }
40184     
40185     this.addEvents({
40186         /**
40187          * @event resize
40188          * Fires when the splitter is moved (alias for {@link #event-moved})
40189          * @param {Roo.bootstrap.SplitBar} this
40190          * @param {Number} newSize the new width or height
40191          */
40192         "resize" : true,
40193         /**
40194          * @event moved
40195          * Fires when the splitter is moved
40196          * @param {Roo.bootstrap.SplitBar} this
40197          * @param {Number} newSize the new width or height
40198          */
40199         "moved" : true,
40200         /**
40201          * @event beforeresize
40202          * Fires before the splitter is dragged
40203          * @param {Roo.bootstrap.SplitBar} this
40204          */
40205         "beforeresize" : true,
40206
40207         "beforeapply" : true
40208     });
40209
40210     Roo.util.Observable.call(this);
40211 };
40212
40213 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
40214     onStartProxyDrag : function(x, y){
40215         this.fireEvent("beforeresize", this);
40216         if(!this.overlay){
40217             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
40218             o.unselectable();
40219             o.enableDisplayMode("block");
40220             // all splitbars share the same overlay
40221             Roo.bootstrap.SplitBar.prototype.overlay = o;
40222         }
40223         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
40224         this.overlay.show();
40225         Roo.get(this.proxy).setDisplayed("block");
40226         var size = this.adapter.getElementSize(this);
40227         this.activeMinSize = this.getMinimumSize();;
40228         this.activeMaxSize = this.getMaximumSize();;
40229         var c1 = size - this.activeMinSize;
40230         var c2 = Math.max(this.activeMaxSize - size, 0);
40231         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40232             this.dd.resetConstraints();
40233             this.dd.setXConstraint(
40234                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
40235                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
40236             );
40237             this.dd.setYConstraint(0, 0);
40238         }else{
40239             this.dd.resetConstraints();
40240             this.dd.setXConstraint(0, 0);
40241             this.dd.setYConstraint(
40242                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
40243                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
40244             );
40245          }
40246         this.dragSpecs.startSize = size;
40247         this.dragSpecs.startPoint = [x, y];
40248         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
40249     },
40250     
40251     /** 
40252      * @private Called after the drag operation by the DDProxy
40253      */
40254     onEndProxyDrag : function(e){
40255         Roo.get(this.proxy).setDisplayed(false);
40256         var endPoint = Roo.lib.Event.getXY(e);
40257         if(this.overlay){
40258             this.overlay.hide();
40259         }
40260         var newSize;
40261         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40262             newSize = this.dragSpecs.startSize + 
40263                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
40264                     endPoint[0] - this.dragSpecs.startPoint[0] :
40265                     this.dragSpecs.startPoint[0] - endPoint[0]
40266                 );
40267         }else{
40268             newSize = this.dragSpecs.startSize + 
40269                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
40270                     endPoint[1] - this.dragSpecs.startPoint[1] :
40271                     this.dragSpecs.startPoint[1] - endPoint[1]
40272                 );
40273         }
40274         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
40275         if(newSize != this.dragSpecs.startSize){
40276             if(this.fireEvent('beforeapply', this, newSize) !== false){
40277                 this.adapter.setElementSize(this, newSize);
40278                 this.fireEvent("moved", this, newSize);
40279                 this.fireEvent("resize", this, newSize);
40280             }
40281         }
40282     },
40283     
40284     /**
40285      * Get the adapter this SplitBar uses
40286      * @return The adapter object
40287      */
40288     getAdapter : function(){
40289         return this.adapter;
40290     },
40291     
40292     /**
40293      * Set the adapter this SplitBar uses
40294      * @param {Object} adapter A SplitBar adapter object
40295      */
40296     setAdapter : function(adapter){
40297         this.adapter = adapter;
40298         this.adapter.init(this);
40299     },
40300     
40301     /**
40302      * Gets the minimum size for the resizing element
40303      * @return {Number} The minimum size
40304      */
40305     getMinimumSize : function(){
40306         return this.minSize;
40307     },
40308     
40309     /**
40310      * Sets the minimum size for the resizing element
40311      * @param {Number} minSize The minimum size
40312      */
40313     setMinimumSize : function(minSize){
40314         this.minSize = minSize;
40315     },
40316     
40317     /**
40318      * Gets the maximum size for the resizing element
40319      * @return {Number} The maximum size
40320      */
40321     getMaximumSize : function(){
40322         return this.maxSize;
40323     },
40324     
40325     /**
40326      * Sets the maximum size for the resizing element
40327      * @param {Number} maxSize The maximum size
40328      */
40329     setMaximumSize : function(maxSize){
40330         this.maxSize = maxSize;
40331     },
40332     
40333     /**
40334      * Sets the initialize size for the resizing element
40335      * @param {Number} size The initial size
40336      */
40337     setCurrentSize : function(size){
40338         var oldAnimate = this.animate;
40339         this.animate = false;
40340         this.adapter.setElementSize(this, size);
40341         this.animate = oldAnimate;
40342     },
40343     
40344     /**
40345      * Destroy this splitbar. 
40346      * @param {Boolean} removeEl True to remove the element
40347      */
40348     destroy : function(removeEl){
40349         if(this.shim){
40350             this.shim.remove();
40351         }
40352         this.dd.unreg();
40353         this.proxy.parentNode.removeChild(this.proxy);
40354         if(removeEl){
40355             this.el.remove();
40356         }
40357     }
40358 });
40359
40360 /**
40361  * @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.
40362  */
40363 Roo.bootstrap.SplitBar.createProxy = function(dir){
40364     var proxy = new Roo.Element(document.createElement("div"));
40365     proxy.unselectable();
40366     var cls = 'roo-splitbar-proxy';
40367     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
40368     document.body.appendChild(proxy.dom);
40369     return proxy.dom;
40370 };
40371
40372 /** 
40373  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
40374  * Default Adapter. It assumes the splitter and resizing element are not positioned
40375  * elements and only gets/sets the width of the element. Generally used for table based layouts.
40376  */
40377 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
40378 };
40379
40380 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
40381     // do nothing for now
40382     init : function(s){
40383     
40384     },
40385     /**
40386      * Called before drag operations to get the current size of the resizing element. 
40387      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40388      */
40389      getElementSize : function(s){
40390         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40391             return s.resizingEl.getWidth();
40392         }else{
40393             return s.resizingEl.getHeight();
40394         }
40395     },
40396     
40397     /**
40398      * Called after drag operations to set the size of the resizing element.
40399      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40400      * @param {Number} newSize The new size to set
40401      * @param {Function} onComplete A function to be invoked when resizing is complete
40402      */
40403     setElementSize : function(s, newSize, onComplete){
40404         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40405             if(!s.animate){
40406                 s.resizingEl.setWidth(newSize);
40407                 if(onComplete){
40408                     onComplete(s, newSize);
40409                 }
40410             }else{
40411                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
40412             }
40413         }else{
40414             
40415             if(!s.animate){
40416                 s.resizingEl.setHeight(newSize);
40417                 if(onComplete){
40418                     onComplete(s, newSize);
40419                 }
40420             }else{
40421                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
40422             }
40423         }
40424     }
40425 };
40426
40427 /** 
40428  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
40429  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
40430  * Adapter that  moves the splitter element to align with the resized sizing element. 
40431  * Used with an absolute positioned SplitBar.
40432  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
40433  * document.body, make sure you assign an id to the body element.
40434  */
40435 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
40436     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40437     this.container = Roo.get(container);
40438 };
40439
40440 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
40441     init : function(s){
40442         this.basic.init(s);
40443     },
40444     
40445     getElementSize : function(s){
40446         return this.basic.getElementSize(s);
40447     },
40448     
40449     setElementSize : function(s, newSize, onComplete){
40450         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
40451     },
40452     
40453     moveSplitter : function(s){
40454         var yes = Roo.bootstrap.SplitBar;
40455         switch(s.placement){
40456             case yes.LEFT:
40457                 s.el.setX(s.resizingEl.getRight());
40458                 break;
40459             case yes.RIGHT:
40460                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
40461                 break;
40462             case yes.TOP:
40463                 s.el.setY(s.resizingEl.getBottom());
40464                 break;
40465             case yes.BOTTOM:
40466                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
40467                 break;
40468         }
40469     }
40470 };
40471
40472 /**
40473  * Orientation constant - Create a vertical SplitBar
40474  * @static
40475  * @type Number
40476  */
40477 Roo.bootstrap.SplitBar.VERTICAL = 1;
40478
40479 /**
40480  * Orientation constant - Create a horizontal SplitBar
40481  * @static
40482  * @type Number
40483  */
40484 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
40485
40486 /**
40487  * Placement constant - The resizing element is to the left of the splitter element
40488  * @static
40489  * @type Number
40490  */
40491 Roo.bootstrap.SplitBar.LEFT = 1;
40492
40493 /**
40494  * Placement constant - The resizing element is to the right of the splitter element
40495  * @static
40496  * @type Number
40497  */
40498 Roo.bootstrap.SplitBar.RIGHT = 2;
40499
40500 /**
40501  * Placement constant - The resizing element is positioned above the splitter element
40502  * @static
40503  * @type Number
40504  */
40505 Roo.bootstrap.SplitBar.TOP = 3;
40506
40507 /**
40508  * Placement constant - The resizing element is positioned under splitter element
40509  * @static
40510  * @type Number
40511  */
40512 Roo.bootstrap.SplitBar.BOTTOM = 4;
40513 /*
40514  * Based on:
40515  * Ext JS Library 1.1.1
40516  * Copyright(c) 2006-2007, Ext JS, LLC.
40517  *
40518  * Originally Released Under LGPL - original licence link has changed is not relivant.
40519  *
40520  * Fork - LGPL
40521  * <script type="text/javascript">
40522  */
40523
40524 /**
40525  * @class Roo.bootstrap.layout.Manager
40526  * @extends Roo.bootstrap.Component
40527  * @abstract
40528  * Base class for layout managers.
40529  */
40530 Roo.bootstrap.layout.Manager = function(config)
40531 {
40532     this.monitorWindowResize = true; // do this before we apply configuration.
40533     
40534     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
40535
40536
40537
40538
40539
40540     /** false to disable window resize monitoring @type Boolean */
40541     
40542     this.regions = {};
40543     this.addEvents({
40544         /**
40545          * @event layout
40546          * Fires when a layout is performed.
40547          * @param {Roo.LayoutManager} this
40548          */
40549         "layout" : true,
40550         /**
40551          * @event regionresized
40552          * Fires when the user resizes a region.
40553          * @param {Roo.LayoutRegion} region The resized region
40554          * @param {Number} newSize The new size (width for east/west, height for north/south)
40555          */
40556         "regionresized" : true,
40557         /**
40558          * @event regioncollapsed
40559          * Fires when a region is collapsed.
40560          * @param {Roo.LayoutRegion} region The collapsed region
40561          */
40562         "regioncollapsed" : true,
40563         /**
40564          * @event regionexpanded
40565          * Fires when a region is expanded.
40566          * @param {Roo.LayoutRegion} region The expanded region
40567          */
40568         "regionexpanded" : true
40569     });
40570     this.updating = false;
40571
40572     if (config.el) {
40573         this.el = Roo.get(config.el);
40574         this.initEvents();
40575     }
40576
40577 };
40578
40579 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
40580
40581
40582     regions : null,
40583
40584     monitorWindowResize : true,
40585
40586
40587     updating : false,
40588
40589
40590     onRender : function(ct, position)
40591     {
40592         if(!this.el){
40593             this.el = Roo.get(ct);
40594             this.initEvents();
40595         }
40596         //this.fireEvent('render',this);
40597     },
40598
40599
40600     initEvents: function()
40601     {
40602
40603
40604         // ie scrollbar fix
40605         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
40606             document.body.scroll = "no";
40607         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
40608             this.el.position('relative');
40609         }
40610         this.id = this.el.id;
40611         this.el.addClass("roo-layout-container");
40612         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
40613         if(this.el.dom != document.body ) {
40614             this.el.on('resize', this.layout,this);
40615             this.el.on('show', this.layout,this);
40616         }
40617
40618     },
40619
40620     /**
40621      * Returns true if this layout is currently being updated
40622      * @return {Boolean}
40623      */
40624     isUpdating : function(){
40625         return this.updating;
40626     },
40627
40628     /**
40629      * Suspend the LayoutManager from doing auto-layouts while
40630      * making multiple add or remove calls
40631      */
40632     beginUpdate : function(){
40633         this.updating = true;
40634     },
40635
40636     /**
40637      * Restore auto-layouts and optionally disable the manager from performing a layout
40638      * @param {Boolean} noLayout true to disable a layout update
40639      */
40640     endUpdate : function(noLayout){
40641         this.updating = false;
40642         if(!noLayout){
40643             this.layout();
40644         }
40645     },
40646
40647     layout: function(){
40648         // abstract...
40649     },
40650
40651     onRegionResized : function(region, newSize){
40652         this.fireEvent("regionresized", region, newSize);
40653         this.layout();
40654     },
40655
40656     onRegionCollapsed : function(region){
40657         this.fireEvent("regioncollapsed", region);
40658     },
40659
40660     onRegionExpanded : function(region){
40661         this.fireEvent("regionexpanded", region);
40662     },
40663
40664     /**
40665      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
40666      * performs box-model adjustments.
40667      * @return {Object} The size as an object {width: (the width), height: (the height)}
40668      */
40669     getViewSize : function()
40670     {
40671         var size;
40672         if(this.el.dom != document.body){
40673             size = this.el.getSize();
40674         }else{
40675             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
40676         }
40677         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
40678         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40679         return size;
40680     },
40681
40682     /**
40683      * Returns the Element this layout is bound to.
40684      * @return {Roo.Element}
40685      */
40686     getEl : function(){
40687         return this.el;
40688     },
40689
40690     /**
40691      * Returns the specified region.
40692      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
40693      * @return {Roo.LayoutRegion}
40694      */
40695     getRegion : function(target){
40696         return this.regions[target.toLowerCase()];
40697     },
40698
40699     onWindowResize : function(){
40700         if(this.monitorWindowResize){
40701             this.layout();
40702         }
40703     }
40704 });
40705 /*
40706  * Based on:
40707  * Ext JS Library 1.1.1
40708  * Copyright(c) 2006-2007, Ext JS, LLC.
40709  *
40710  * Originally Released Under LGPL - original licence link has changed is not relivant.
40711  *
40712  * Fork - LGPL
40713  * <script type="text/javascript">
40714  */
40715 /**
40716  * @class Roo.bootstrap.layout.Border
40717  * @extends Roo.bootstrap.layout.Manager
40718  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
40719  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
40720  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
40721  * please see: examples/bootstrap/nested.html<br><br>
40722  
40723 <b>The container the layout is rendered into can be either the body element or any other element.
40724 If it is not the body element, the container needs to either be an absolute positioned element,
40725 or you will need to add "position:relative" to the css of the container.  You will also need to specify
40726 the container size if it is not the body element.</b>
40727
40728 * @constructor
40729 * Create a new Border
40730 * @param {Object} config Configuration options
40731  */
40732 Roo.bootstrap.layout.Border = function(config){
40733     config = config || {};
40734     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
40735     
40736     
40737     
40738     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
40739         if(config[region]){
40740             config[region].region = region;
40741             this.addRegion(config[region]);
40742         }
40743     },this);
40744     
40745 };
40746
40747 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
40748
40749 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
40750     
40751         /**
40752          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
40753          */
40754         /**
40755          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
40756          */
40757         /**
40758          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
40759          */
40760         /**
40761          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
40762          */
40763         /**
40764          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
40765          */
40766         
40767         
40768         
40769         
40770     parent : false, // this might point to a 'nest' or a ???
40771     
40772     /**
40773      * Creates and adds a new region if it doesn't already exist.
40774      * @param {String} target The target region key (north, south, east, west or center).
40775      * @param {Object} config The regions config object
40776      * @return {BorderLayoutRegion} The new region
40777      */
40778     addRegion : function(config)
40779     {
40780         if(!this.regions[config.region]){
40781             var r = this.factory(config);
40782             this.bindRegion(r);
40783         }
40784         return this.regions[config.region];
40785     },
40786
40787     // private (kinda)
40788     bindRegion : function(r){
40789         this.regions[r.config.region] = r;
40790         
40791         r.on("visibilitychange",    this.layout, this);
40792         r.on("paneladded",          this.layout, this);
40793         r.on("panelremoved",        this.layout, this);
40794         r.on("invalidated",         this.layout, this);
40795         r.on("resized",             this.onRegionResized, this);
40796         r.on("collapsed",           this.onRegionCollapsed, this);
40797         r.on("expanded",            this.onRegionExpanded, this);
40798     },
40799
40800     /**
40801      * Performs a layout update.
40802      */
40803     layout : function()
40804     {
40805         if(this.updating) {
40806             return;
40807         }
40808         
40809         // render all the rebions if they have not been done alreayd?
40810         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
40811             if(this.regions[region] && !this.regions[region].bodyEl){
40812                 this.regions[region].onRender(this.el)
40813             }
40814         },this);
40815         
40816         var size = this.getViewSize();
40817         var w = size.width;
40818         var h = size.height;
40819         var centerW = w;
40820         var centerH = h;
40821         var centerY = 0;
40822         var centerX = 0;
40823         //var x = 0, y = 0;
40824
40825         var rs = this.regions;
40826         var north = rs["north"];
40827         var south = rs["south"]; 
40828         var west = rs["west"];
40829         var east = rs["east"];
40830         var center = rs["center"];
40831         //if(this.hideOnLayout){ // not supported anymore
40832             //c.el.setStyle("display", "none");
40833         //}
40834         if(north && north.isVisible()){
40835             var b = north.getBox();
40836             var m = north.getMargins();
40837             b.width = w - (m.left+m.right);
40838             b.x = m.left;
40839             b.y = m.top;
40840             centerY = b.height + b.y + m.bottom;
40841             centerH -= centerY;
40842             north.updateBox(this.safeBox(b));
40843         }
40844         if(south && south.isVisible()){
40845             var b = south.getBox();
40846             var m = south.getMargins();
40847             b.width = w - (m.left+m.right);
40848             b.x = m.left;
40849             var totalHeight = (b.height + m.top + m.bottom);
40850             b.y = h - totalHeight + m.top;
40851             centerH -= totalHeight;
40852             south.updateBox(this.safeBox(b));
40853         }
40854         if(west && west.isVisible()){
40855             var b = west.getBox();
40856             var m = west.getMargins();
40857             b.height = centerH - (m.top+m.bottom);
40858             b.x = m.left;
40859             b.y = centerY + m.top;
40860             var totalWidth = (b.width + m.left + m.right);
40861             centerX += totalWidth;
40862             centerW -= totalWidth;
40863             west.updateBox(this.safeBox(b));
40864         }
40865         if(east && east.isVisible()){
40866             var b = east.getBox();
40867             var m = east.getMargins();
40868             b.height = centerH - (m.top+m.bottom);
40869             var totalWidth = (b.width + m.left + m.right);
40870             b.x = w - totalWidth + m.left;
40871             b.y = centerY + m.top;
40872             centerW -= totalWidth;
40873             east.updateBox(this.safeBox(b));
40874         }
40875         if(center){
40876             var m = center.getMargins();
40877             var centerBox = {
40878                 x: centerX + m.left,
40879                 y: centerY + m.top,
40880                 width: centerW - (m.left+m.right),
40881                 height: centerH - (m.top+m.bottom)
40882             };
40883             //if(this.hideOnLayout){
40884                 //center.el.setStyle("display", "block");
40885             //}
40886             center.updateBox(this.safeBox(centerBox));
40887         }
40888         this.el.repaint();
40889         this.fireEvent("layout", this);
40890     },
40891
40892     // private
40893     safeBox : function(box){
40894         box.width = Math.max(0, box.width);
40895         box.height = Math.max(0, box.height);
40896         return box;
40897     },
40898
40899     /**
40900      * Adds a ContentPanel (or subclass) to this layout.
40901      * @param {String} target The target region key (north, south, east, west or center).
40902      * @param {Roo.ContentPanel} panel The panel to add
40903      * @return {Roo.ContentPanel} The added panel
40904      */
40905     add : function(target, panel){
40906          
40907         target = target.toLowerCase();
40908         return this.regions[target].add(panel);
40909     },
40910
40911     /**
40912      * Remove a ContentPanel (or subclass) to this layout.
40913      * @param {String} target The target region key (north, south, east, west or center).
40914      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
40915      * @return {Roo.ContentPanel} The removed panel
40916      */
40917     remove : function(target, panel){
40918         target = target.toLowerCase();
40919         return this.regions[target].remove(panel);
40920     },
40921
40922     /**
40923      * Searches all regions for a panel with the specified id
40924      * @param {String} panelId
40925      * @return {Roo.ContentPanel} The panel or null if it wasn't found
40926      */
40927     findPanel : function(panelId){
40928         var rs = this.regions;
40929         for(var target in rs){
40930             if(typeof rs[target] != "function"){
40931                 var p = rs[target].getPanel(panelId);
40932                 if(p){
40933                     return p;
40934                 }
40935             }
40936         }
40937         return null;
40938     },
40939
40940     /**
40941      * Searches all regions for a panel with the specified id and activates (shows) it.
40942      * @param {String/ContentPanel} panelId The panels id or the panel itself
40943      * @return {Roo.ContentPanel} The shown panel or null
40944      */
40945     showPanel : function(panelId) {
40946       var rs = this.regions;
40947       for(var target in rs){
40948          var r = rs[target];
40949          if(typeof r != "function"){
40950             if(r.hasPanel(panelId)){
40951                return r.showPanel(panelId);
40952             }
40953          }
40954       }
40955       return null;
40956    },
40957
40958    /**
40959      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
40960      * @param {Roo.state.Provider} provider (optional) An alternate state provider
40961      */
40962    /*
40963     restoreState : function(provider){
40964         if(!provider){
40965             provider = Roo.state.Manager;
40966         }
40967         var sm = new Roo.LayoutStateManager();
40968         sm.init(this, provider);
40969     },
40970 */
40971  
40972  
40973     /**
40974      * Adds a xtype elements to the layout.
40975      * <pre><code>
40976
40977 layout.addxtype({
40978        xtype : 'ContentPanel',
40979        region: 'west',
40980        items: [ .... ]
40981    }
40982 );
40983
40984 layout.addxtype({
40985         xtype : 'NestedLayoutPanel',
40986         region: 'west',
40987         layout: {
40988            center: { },
40989            west: { }   
40990         },
40991         items : [ ... list of content panels or nested layout panels.. ]
40992    }
40993 );
40994 </code></pre>
40995      * @param {Object} cfg Xtype definition of item to add.
40996      */
40997     addxtype : function(cfg)
40998     {
40999         // basically accepts a pannel...
41000         // can accept a layout region..!?!?
41001         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
41002         
41003         
41004         // theory?  children can only be panels??
41005         
41006         //if (!cfg.xtype.match(/Panel$/)) {
41007         //    return false;
41008         //}
41009         var ret = false;
41010         
41011         if (typeof(cfg.region) == 'undefined') {
41012             Roo.log("Failed to add Panel, region was not set");
41013             Roo.log(cfg);
41014             return false;
41015         }
41016         var region = cfg.region;
41017         delete cfg.region;
41018         
41019           
41020         var xitems = [];
41021         if (cfg.items) {
41022             xitems = cfg.items;
41023             delete cfg.items;
41024         }
41025         var nb = false;
41026         
41027         if ( region == 'center') {
41028             Roo.log("Center: " + cfg.title);
41029         }
41030         
41031         
41032         switch(cfg.xtype) 
41033         {
41034             case 'Content':  // ContentPanel (el, cfg)
41035             case 'Scroll':  // ContentPanel (el, cfg)
41036             case 'View': 
41037                 cfg.autoCreate = cfg.autoCreate || true;
41038                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41039                 //} else {
41040                 //    var el = this.el.createChild();
41041                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
41042                 //}
41043                 
41044                 this.add(region, ret);
41045                 break;
41046             
41047             /*
41048             case 'TreePanel': // our new panel!
41049                 cfg.el = this.el.createChild();
41050                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41051                 this.add(region, ret);
41052                 break;
41053             */
41054             
41055             case 'Nest': 
41056                 // create a new Layout (which is  a Border Layout...
41057                 
41058                 var clayout = cfg.layout;
41059                 clayout.el  = this.el.createChild();
41060                 clayout.items   = clayout.items  || [];
41061                 
41062                 delete cfg.layout;
41063                 
41064                 // replace this exitems with the clayout ones..
41065                 xitems = clayout.items;
41066                  
41067                 // force background off if it's in center...
41068                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
41069                     cfg.background = false;
41070                 }
41071                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
41072                 
41073                 
41074                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41075                 //console.log('adding nested layout panel '  + cfg.toSource());
41076                 this.add(region, ret);
41077                 nb = {}; /// find first...
41078                 break;
41079             
41080             case 'Grid':
41081                 
41082                 // needs grid and region
41083                 
41084                 //var el = this.getRegion(region).el.createChild();
41085                 /*
41086                  *var el = this.el.createChild();
41087                 // create the grid first...
41088                 cfg.grid.container = el;
41089                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
41090                 */
41091                 
41092                 if (region == 'center' && this.active ) {
41093                     cfg.background = false;
41094                 }
41095                 
41096                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41097                 
41098                 this.add(region, ret);
41099                 /*
41100                 if (cfg.background) {
41101                     // render grid on panel activation (if panel background)
41102                     ret.on('activate', function(gp) {
41103                         if (!gp.grid.rendered) {
41104                     //        gp.grid.render(el);
41105                         }
41106                     });
41107                 } else {
41108                   //  cfg.grid.render(el);
41109                 }
41110                 */
41111                 break;
41112            
41113            
41114             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
41115                 // it was the old xcomponent building that caused this before.
41116                 // espeically if border is the top element in the tree.
41117                 ret = this;
41118                 break; 
41119                 
41120                     
41121                 
41122                 
41123                 
41124             default:
41125                 /*
41126                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
41127                     
41128                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41129                     this.add(region, ret);
41130                 } else {
41131                 */
41132                     Roo.log(cfg);
41133                     throw "Can not add '" + cfg.xtype + "' to Border";
41134                     return null;
41135              
41136                                 
41137              
41138         }
41139         this.beginUpdate();
41140         // add children..
41141         var region = '';
41142         var abn = {};
41143         Roo.each(xitems, function(i)  {
41144             region = nb && i.region ? i.region : false;
41145             
41146             var add = ret.addxtype(i);
41147            
41148             if (region) {
41149                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
41150                 if (!i.background) {
41151                     abn[region] = nb[region] ;
41152                 }
41153             }
41154             
41155         });
41156         this.endUpdate();
41157
41158         // make the last non-background panel active..
41159         //if (nb) { Roo.log(abn); }
41160         if (nb) {
41161             
41162             for(var r in abn) {
41163                 region = this.getRegion(r);
41164                 if (region) {
41165                     // tried using nb[r], but it does not work..
41166                      
41167                     region.showPanel(abn[r]);
41168                    
41169                 }
41170             }
41171         }
41172         return ret;
41173         
41174     },
41175     
41176     
41177 // private
41178     factory : function(cfg)
41179     {
41180         
41181         var validRegions = Roo.bootstrap.layout.Border.regions;
41182
41183         var target = cfg.region;
41184         cfg.mgr = this;
41185         
41186         var r = Roo.bootstrap.layout;
41187         Roo.log(target);
41188         switch(target){
41189             case "north":
41190                 return new r.North(cfg);
41191             case "south":
41192                 return new r.South(cfg);
41193             case "east":
41194                 return new r.East(cfg);
41195             case "west":
41196                 return new r.West(cfg);
41197             case "center":
41198                 return new r.Center(cfg);
41199         }
41200         throw 'Layout region "'+target+'" not supported.';
41201     }
41202     
41203     
41204 });
41205  /*
41206  * Based on:
41207  * Ext JS Library 1.1.1
41208  * Copyright(c) 2006-2007, Ext JS, LLC.
41209  *
41210  * Originally Released Under LGPL - original licence link has changed is not relivant.
41211  *
41212  * Fork - LGPL
41213  * <script type="text/javascript">
41214  */
41215  
41216 /**
41217  * @class Roo.bootstrap.layout.Basic
41218  * @extends Roo.util.Observable
41219  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
41220  * and does not have a titlebar, tabs or any other features. All it does is size and position 
41221  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
41222  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
41223  * @cfg {string}   region  the region that it inhabits..
41224  * @cfg {bool}   skipConfig skip config?
41225  * 
41226
41227  */
41228 Roo.bootstrap.layout.Basic = function(config){
41229     
41230     this.mgr = config.mgr;
41231     
41232     this.position = config.region;
41233     
41234     var skipConfig = config.skipConfig;
41235     
41236     this.events = {
41237         /**
41238          * @scope Roo.BasicLayoutRegion
41239          */
41240         
41241         /**
41242          * @event beforeremove
41243          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
41244          * @param {Roo.LayoutRegion} this
41245          * @param {Roo.ContentPanel} panel The panel
41246          * @param {Object} e The cancel event object
41247          */
41248         "beforeremove" : true,
41249         /**
41250          * @event invalidated
41251          * Fires when the layout for this region is changed.
41252          * @param {Roo.LayoutRegion} this
41253          */
41254         "invalidated" : true,
41255         /**
41256          * @event visibilitychange
41257          * Fires when this region is shown or hidden 
41258          * @param {Roo.LayoutRegion} this
41259          * @param {Boolean} visibility true or false
41260          */
41261         "visibilitychange" : true,
41262         /**
41263          * @event paneladded
41264          * Fires when a panel is added. 
41265          * @param {Roo.LayoutRegion} this
41266          * @param {Roo.ContentPanel} panel The panel
41267          */
41268         "paneladded" : true,
41269         /**
41270          * @event panelremoved
41271          * Fires when a panel is removed. 
41272          * @param {Roo.LayoutRegion} this
41273          * @param {Roo.ContentPanel} panel The panel
41274          */
41275         "panelremoved" : true,
41276         /**
41277          * @event beforecollapse
41278          * Fires when this region before collapse.
41279          * @param {Roo.LayoutRegion} this
41280          */
41281         "beforecollapse" : true,
41282         /**
41283          * @event collapsed
41284          * Fires when this region is collapsed.
41285          * @param {Roo.LayoutRegion} this
41286          */
41287         "collapsed" : true,
41288         /**
41289          * @event expanded
41290          * Fires when this region is expanded.
41291          * @param {Roo.LayoutRegion} this
41292          */
41293         "expanded" : true,
41294         /**
41295          * @event slideshow
41296          * Fires when this region is slid into view.
41297          * @param {Roo.LayoutRegion} this
41298          */
41299         "slideshow" : true,
41300         /**
41301          * @event slidehide
41302          * Fires when this region slides out of view. 
41303          * @param {Roo.LayoutRegion} this
41304          */
41305         "slidehide" : true,
41306         /**
41307          * @event panelactivated
41308          * Fires when a panel is activated. 
41309          * @param {Roo.LayoutRegion} this
41310          * @param {Roo.ContentPanel} panel The activated panel
41311          */
41312         "panelactivated" : true,
41313         /**
41314          * @event resized
41315          * Fires when the user resizes this region. 
41316          * @param {Roo.LayoutRegion} this
41317          * @param {Number} newSize The new size (width for east/west, height for north/south)
41318          */
41319         "resized" : true
41320     };
41321     /** A collection of panels in this region. @type Roo.util.MixedCollection */
41322     this.panels = new Roo.util.MixedCollection();
41323     this.panels.getKey = this.getPanelId.createDelegate(this);
41324     this.box = null;
41325     this.activePanel = null;
41326     // ensure listeners are added...
41327     
41328     if (config.listeners || config.events) {
41329         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
41330             listeners : config.listeners || {},
41331             events : config.events || {}
41332         });
41333     }
41334     
41335     if(skipConfig !== true){
41336         this.applyConfig(config);
41337     }
41338 };
41339
41340 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
41341 {
41342     getPanelId : function(p){
41343         return p.getId();
41344     },
41345     
41346     applyConfig : function(config){
41347         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41348         this.config = config;
41349         
41350     },
41351     
41352     /**
41353      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
41354      * the width, for horizontal (north, south) the height.
41355      * @param {Number} newSize The new width or height
41356      */
41357     resizeTo : function(newSize){
41358         var el = this.el ? this.el :
41359                  (this.activePanel ? this.activePanel.getEl() : null);
41360         if(el){
41361             switch(this.position){
41362                 case "east":
41363                 case "west":
41364                     el.setWidth(newSize);
41365                     this.fireEvent("resized", this, newSize);
41366                 break;
41367                 case "north":
41368                 case "south":
41369                     el.setHeight(newSize);
41370                     this.fireEvent("resized", this, newSize);
41371                 break;                
41372             }
41373         }
41374     },
41375     
41376     getBox : function(){
41377         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
41378     },
41379     
41380     getMargins : function(){
41381         return this.margins;
41382     },
41383     
41384     updateBox : function(box){
41385         this.box = box;
41386         var el = this.activePanel.getEl();
41387         el.dom.style.left = box.x + "px";
41388         el.dom.style.top = box.y + "px";
41389         this.activePanel.setSize(box.width, box.height);
41390     },
41391     
41392     /**
41393      * Returns the container element for this region.
41394      * @return {Roo.Element}
41395      */
41396     getEl : function(){
41397         return this.activePanel;
41398     },
41399     
41400     /**
41401      * Returns true if this region is currently visible.
41402      * @return {Boolean}
41403      */
41404     isVisible : function(){
41405         return this.activePanel ? true : false;
41406     },
41407     
41408     setActivePanel : function(panel){
41409         panel = this.getPanel(panel);
41410         if(this.activePanel && this.activePanel != panel){
41411             this.activePanel.setActiveState(false);
41412             this.activePanel.getEl().setLeftTop(-10000,-10000);
41413         }
41414         this.activePanel = panel;
41415         panel.setActiveState(true);
41416         if(this.box){
41417             panel.setSize(this.box.width, this.box.height);
41418         }
41419         this.fireEvent("panelactivated", this, panel);
41420         this.fireEvent("invalidated");
41421     },
41422     
41423     /**
41424      * Show the specified panel.
41425      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
41426      * @return {Roo.ContentPanel} The shown panel or null
41427      */
41428     showPanel : function(panel){
41429         panel = this.getPanel(panel);
41430         if(panel){
41431             this.setActivePanel(panel);
41432         }
41433         return panel;
41434     },
41435     
41436     /**
41437      * Get the active panel for this region.
41438      * @return {Roo.ContentPanel} The active panel or null
41439      */
41440     getActivePanel : function(){
41441         return this.activePanel;
41442     },
41443     
41444     /**
41445      * Add the passed ContentPanel(s)
41446      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
41447      * @return {Roo.ContentPanel} The panel added (if only one was added)
41448      */
41449     add : function(panel){
41450         if(arguments.length > 1){
41451             for(var i = 0, len = arguments.length; i < len; i++) {
41452                 this.add(arguments[i]);
41453             }
41454             return null;
41455         }
41456         if(this.hasPanel(panel)){
41457             this.showPanel(panel);
41458             return panel;
41459         }
41460         var el = panel.getEl();
41461         if(el.dom.parentNode != this.mgr.el.dom){
41462             this.mgr.el.dom.appendChild(el.dom);
41463         }
41464         if(panel.setRegion){
41465             panel.setRegion(this);
41466         }
41467         this.panels.add(panel);
41468         el.setStyle("position", "absolute");
41469         if(!panel.background){
41470             this.setActivePanel(panel);
41471             if(this.config.initialSize && this.panels.getCount()==1){
41472                 this.resizeTo(this.config.initialSize);
41473             }
41474         }
41475         this.fireEvent("paneladded", this, panel);
41476         return panel;
41477     },
41478     
41479     /**
41480      * Returns true if the panel is in this region.
41481      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41482      * @return {Boolean}
41483      */
41484     hasPanel : function(panel){
41485         if(typeof panel == "object"){ // must be panel obj
41486             panel = panel.getId();
41487         }
41488         return this.getPanel(panel) ? true : false;
41489     },
41490     
41491     /**
41492      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
41493      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41494      * @param {Boolean} preservePanel Overrides the config preservePanel option
41495      * @return {Roo.ContentPanel} The panel that was removed
41496      */
41497     remove : function(panel, preservePanel){
41498         panel = this.getPanel(panel);
41499         if(!panel){
41500             return null;
41501         }
41502         var e = {};
41503         this.fireEvent("beforeremove", this, panel, e);
41504         if(e.cancel === true){
41505             return null;
41506         }
41507         var panelId = panel.getId();
41508         this.panels.removeKey(panelId);
41509         return panel;
41510     },
41511     
41512     /**
41513      * Returns the panel specified or null if it's not in this region.
41514      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41515      * @return {Roo.ContentPanel}
41516      */
41517     getPanel : function(id){
41518         if(typeof id == "object"){ // must be panel obj
41519             return id;
41520         }
41521         return this.panels.get(id);
41522     },
41523     
41524     /**
41525      * Returns this regions position (north/south/east/west/center).
41526      * @return {String} 
41527      */
41528     getPosition: function(){
41529         return this.position;    
41530     }
41531 });/*
41532  * Based on:
41533  * Ext JS Library 1.1.1
41534  * Copyright(c) 2006-2007, Ext JS, LLC.
41535  *
41536  * Originally Released Under LGPL - original licence link has changed is not relivant.
41537  *
41538  * Fork - LGPL
41539  * <script type="text/javascript">
41540  */
41541  
41542 /**
41543  * @class Roo.bootstrap.layout.Region
41544  * @extends Roo.bootstrap.layout.Basic
41545  * This class represents a region in a layout manager.
41546  
41547  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
41548  * @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})
41549  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
41550  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
41551  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
41552  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
41553  * @cfg {String}    title           The title for the region (overrides panel titles)
41554  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
41555  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
41556  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
41557  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
41558  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
41559  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
41560  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
41561  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
41562  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
41563  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
41564
41565  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
41566  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
41567  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
41568  * @cfg {Number}    width           For East/West panels
41569  * @cfg {Number}    height          For North/South panels
41570  * @cfg {Boolean}   split           To show the splitter
41571  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
41572  * 
41573  * @cfg {string}   cls             Extra CSS classes to add to region
41574  * 
41575  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
41576  * @cfg {string}   region  the region that it inhabits..
41577  *
41578
41579  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
41580  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
41581
41582  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
41583  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
41584  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
41585  */
41586 Roo.bootstrap.layout.Region = function(config)
41587 {
41588     this.applyConfig(config);
41589
41590     var mgr = config.mgr;
41591     var pos = config.region;
41592     config.skipConfig = true;
41593     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
41594     
41595     if (mgr.el) {
41596         this.onRender(mgr.el);   
41597     }
41598      
41599     this.visible = true;
41600     this.collapsed = false;
41601     this.unrendered_panels = [];
41602 };
41603
41604 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
41605
41606     position: '', // set by wrapper (eg. north/south etc..)
41607     unrendered_panels : null,  // unrendered panels.
41608     
41609     tabPosition : false,
41610     
41611     mgr: false, // points to 'Border'
41612     
41613     
41614     createBody : function(){
41615         /** This region's body element 
41616         * @type Roo.Element */
41617         this.bodyEl = this.el.createChild({
41618                 tag: "div",
41619                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
41620         });
41621     },
41622
41623     onRender: function(ctr, pos)
41624     {
41625         var dh = Roo.DomHelper;
41626         /** This region's container element 
41627         * @type Roo.Element */
41628         this.el = dh.append(ctr.dom, {
41629                 tag: "div",
41630                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
41631             }, true);
41632         /** This region's title element 
41633         * @type Roo.Element */
41634     
41635         this.titleEl = dh.append(this.el.dom,  {
41636                 tag: "div",
41637                 unselectable: "on",
41638                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
41639                 children:[
41640                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
41641                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
41642                 ]
41643             }, true);
41644         
41645         this.titleEl.enableDisplayMode();
41646         /** This region's title text element 
41647         * @type HTMLElement */
41648         this.titleTextEl = this.titleEl.dom.firstChild;
41649         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
41650         /*
41651         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
41652         this.closeBtn.enableDisplayMode();
41653         this.closeBtn.on("click", this.closeClicked, this);
41654         this.closeBtn.hide();
41655     */
41656         this.createBody(this.config);
41657         if(this.config.hideWhenEmpty){
41658             this.hide();
41659             this.on("paneladded", this.validateVisibility, this);
41660             this.on("panelremoved", this.validateVisibility, this);
41661         }
41662         if(this.autoScroll){
41663             this.bodyEl.setStyle("overflow", "auto");
41664         }else{
41665             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
41666         }
41667         //if(c.titlebar !== false){
41668             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
41669                 this.titleEl.hide();
41670             }else{
41671                 this.titleEl.show();
41672                 if(this.config.title){
41673                     this.titleTextEl.innerHTML = this.config.title;
41674                 }
41675             }
41676         //}
41677         if(this.config.collapsed){
41678             this.collapse(true);
41679         }
41680         if(this.config.hidden){
41681             this.hide();
41682         }
41683         
41684         if (this.unrendered_panels && this.unrendered_panels.length) {
41685             for (var i =0;i< this.unrendered_panels.length; i++) {
41686                 this.add(this.unrendered_panels[i]);
41687             }
41688             this.unrendered_panels = null;
41689             
41690         }
41691         
41692     },
41693     
41694     applyConfig : function(c)
41695     {
41696         /*
41697          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
41698             var dh = Roo.DomHelper;
41699             if(c.titlebar !== false){
41700                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
41701                 this.collapseBtn.on("click", this.collapse, this);
41702                 this.collapseBtn.enableDisplayMode();
41703                 /*
41704                 if(c.showPin === true || this.showPin){
41705                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
41706                     this.stickBtn.enableDisplayMode();
41707                     this.stickBtn.on("click", this.expand, this);
41708                     this.stickBtn.hide();
41709                 }
41710                 
41711             }
41712             */
41713             /** This region's collapsed element
41714             * @type Roo.Element */
41715             /*
41716              *
41717             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
41718                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
41719             ]}, true);
41720             
41721             if(c.floatable !== false){
41722                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
41723                this.collapsedEl.on("click", this.collapseClick, this);
41724             }
41725
41726             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
41727                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
41728                    id: "message", unselectable: "on", style:{"float":"left"}});
41729                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
41730              }
41731             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
41732             this.expandBtn.on("click", this.expand, this);
41733             
41734         }
41735         
41736         if(this.collapseBtn){
41737             this.collapseBtn.setVisible(c.collapsible == true);
41738         }
41739         
41740         this.cmargins = c.cmargins || this.cmargins ||
41741                          (this.position == "west" || this.position == "east" ?
41742                              {top: 0, left: 2, right:2, bottom: 0} :
41743                              {top: 2, left: 0, right:0, bottom: 2});
41744         */
41745         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41746         
41747         
41748         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
41749         
41750         this.autoScroll = c.autoScroll || false;
41751         
41752         
41753        
41754         
41755         this.duration = c.duration || .30;
41756         this.slideDuration = c.slideDuration || .45;
41757         this.config = c;
41758        
41759     },
41760     /**
41761      * Returns true if this region is currently visible.
41762      * @return {Boolean}
41763      */
41764     isVisible : function(){
41765         return this.visible;
41766     },
41767
41768     /**
41769      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
41770      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
41771      */
41772     //setCollapsedTitle : function(title){
41773     //    title = title || "&#160;";
41774      //   if(this.collapsedTitleTextEl){
41775       //      this.collapsedTitleTextEl.innerHTML = title;
41776        // }
41777     //},
41778
41779     getBox : function(){
41780         var b;
41781       //  if(!this.collapsed){
41782             b = this.el.getBox(false, true);
41783        // }else{
41784           //  b = this.collapsedEl.getBox(false, true);
41785         //}
41786         return b;
41787     },
41788
41789     getMargins : function(){
41790         return this.margins;
41791         //return this.collapsed ? this.cmargins : this.margins;
41792     },
41793 /*
41794     highlight : function(){
41795         this.el.addClass("x-layout-panel-dragover");
41796     },
41797
41798     unhighlight : function(){
41799         this.el.removeClass("x-layout-panel-dragover");
41800     },
41801 */
41802     updateBox : function(box)
41803     {
41804         if (!this.bodyEl) {
41805             return; // not rendered yet..
41806         }
41807         
41808         this.box = box;
41809         if(!this.collapsed){
41810             this.el.dom.style.left = box.x + "px";
41811             this.el.dom.style.top = box.y + "px";
41812             this.updateBody(box.width, box.height);
41813         }else{
41814             this.collapsedEl.dom.style.left = box.x + "px";
41815             this.collapsedEl.dom.style.top = box.y + "px";
41816             this.collapsedEl.setSize(box.width, box.height);
41817         }
41818         if(this.tabs){
41819             this.tabs.autoSizeTabs();
41820         }
41821     },
41822
41823     updateBody : function(w, h)
41824     {
41825         if(w !== null){
41826             this.el.setWidth(w);
41827             w -= this.el.getBorderWidth("rl");
41828             if(this.config.adjustments){
41829                 w += this.config.adjustments[0];
41830             }
41831         }
41832         if(h !== null && h > 0){
41833             this.el.setHeight(h);
41834             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
41835             h -= this.el.getBorderWidth("tb");
41836             if(this.config.adjustments){
41837                 h += this.config.adjustments[1];
41838             }
41839             this.bodyEl.setHeight(h);
41840             if(this.tabs){
41841                 h = this.tabs.syncHeight(h);
41842             }
41843         }
41844         if(this.panelSize){
41845             w = w !== null ? w : this.panelSize.width;
41846             h = h !== null ? h : this.panelSize.height;
41847         }
41848         if(this.activePanel){
41849             var el = this.activePanel.getEl();
41850             w = w !== null ? w : el.getWidth();
41851             h = h !== null ? h : el.getHeight();
41852             this.panelSize = {width: w, height: h};
41853             this.activePanel.setSize(w, h);
41854         }
41855         if(Roo.isIE && this.tabs){
41856             this.tabs.el.repaint();
41857         }
41858     },
41859
41860     /**
41861      * Returns the container element for this region.
41862      * @return {Roo.Element}
41863      */
41864     getEl : function(){
41865         return this.el;
41866     },
41867
41868     /**
41869      * Hides this region.
41870      */
41871     hide : function(){
41872         //if(!this.collapsed){
41873             this.el.dom.style.left = "-2000px";
41874             this.el.hide();
41875         //}else{
41876          //   this.collapsedEl.dom.style.left = "-2000px";
41877          //   this.collapsedEl.hide();
41878        // }
41879         this.visible = false;
41880         this.fireEvent("visibilitychange", this, false);
41881     },
41882
41883     /**
41884      * Shows this region if it was previously hidden.
41885      */
41886     show : function(){
41887         //if(!this.collapsed){
41888             this.el.show();
41889         //}else{
41890         //    this.collapsedEl.show();
41891        // }
41892         this.visible = true;
41893         this.fireEvent("visibilitychange", this, true);
41894     },
41895 /*
41896     closeClicked : function(){
41897         if(this.activePanel){
41898             this.remove(this.activePanel);
41899         }
41900     },
41901
41902     collapseClick : function(e){
41903         if(this.isSlid){
41904            e.stopPropagation();
41905            this.slideIn();
41906         }else{
41907            e.stopPropagation();
41908            this.slideOut();
41909         }
41910     },
41911 */
41912     /**
41913      * Collapses this region.
41914      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
41915      */
41916     /*
41917     collapse : function(skipAnim, skipCheck = false){
41918         if(this.collapsed) {
41919             return;
41920         }
41921         
41922         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
41923             
41924             this.collapsed = true;
41925             if(this.split){
41926                 this.split.el.hide();
41927             }
41928             if(this.config.animate && skipAnim !== true){
41929                 this.fireEvent("invalidated", this);
41930                 this.animateCollapse();
41931             }else{
41932                 this.el.setLocation(-20000,-20000);
41933                 this.el.hide();
41934                 this.collapsedEl.show();
41935                 this.fireEvent("collapsed", this);
41936                 this.fireEvent("invalidated", this);
41937             }
41938         }
41939         
41940     },
41941 */
41942     animateCollapse : function(){
41943         // overridden
41944     },
41945
41946     /**
41947      * Expands this region if it was previously collapsed.
41948      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
41949      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
41950      */
41951     /*
41952     expand : function(e, skipAnim){
41953         if(e) {
41954             e.stopPropagation();
41955         }
41956         if(!this.collapsed || this.el.hasActiveFx()) {
41957             return;
41958         }
41959         if(this.isSlid){
41960             this.afterSlideIn();
41961             skipAnim = true;
41962         }
41963         this.collapsed = false;
41964         if(this.config.animate && skipAnim !== true){
41965             this.animateExpand();
41966         }else{
41967             this.el.show();
41968             if(this.split){
41969                 this.split.el.show();
41970             }
41971             this.collapsedEl.setLocation(-2000,-2000);
41972             this.collapsedEl.hide();
41973             this.fireEvent("invalidated", this);
41974             this.fireEvent("expanded", this);
41975         }
41976     },
41977 */
41978     animateExpand : function(){
41979         // overridden
41980     },
41981
41982     initTabs : function()
41983     {
41984         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
41985         
41986         var ts = new Roo.bootstrap.panel.Tabs({
41987             el: this.bodyEl.dom,
41988             region : this,
41989             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
41990             disableTooltips: this.config.disableTabTips,
41991             toolbar : this.config.toolbar
41992         });
41993         
41994         if(this.config.hideTabs){
41995             ts.stripWrap.setDisplayed(false);
41996         }
41997         this.tabs = ts;
41998         ts.resizeTabs = this.config.resizeTabs === true;
41999         ts.minTabWidth = this.config.minTabWidth || 40;
42000         ts.maxTabWidth = this.config.maxTabWidth || 250;
42001         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
42002         ts.monitorResize = false;
42003         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
42004         ts.bodyEl.addClass('roo-layout-tabs-body');
42005         this.panels.each(this.initPanelAsTab, this);
42006     },
42007
42008     initPanelAsTab : function(panel){
42009         var ti = this.tabs.addTab(
42010             panel.getEl().id,
42011             panel.getTitle(),
42012             null,
42013             this.config.closeOnTab && panel.isClosable(),
42014             panel.tpl
42015         );
42016         if(panel.tabTip !== undefined){
42017             ti.setTooltip(panel.tabTip);
42018         }
42019         ti.on("activate", function(){
42020               this.setActivePanel(panel);
42021         }, this);
42022         
42023         if(this.config.closeOnTab){
42024             ti.on("beforeclose", function(t, e){
42025                 e.cancel = true;
42026                 this.remove(panel);
42027             }, this);
42028         }
42029         
42030         panel.tabItem = ti;
42031         
42032         return ti;
42033     },
42034
42035     updatePanelTitle : function(panel, title)
42036     {
42037         if(this.activePanel == panel){
42038             this.updateTitle(title);
42039         }
42040         if(this.tabs){
42041             var ti = this.tabs.getTab(panel.getEl().id);
42042             ti.setText(title);
42043             if(panel.tabTip !== undefined){
42044                 ti.setTooltip(panel.tabTip);
42045             }
42046         }
42047     },
42048
42049     updateTitle : function(title){
42050         if(this.titleTextEl && !this.config.title){
42051             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
42052         }
42053     },
42054
42055     setActivePanel : function(panel)
42056     {
42057         panel = this.getPanel(panel);
42058         if(this.activePanel && this.activePanel != panel){
42059             if(this.activePanel.setActiveState(false) === false){
42060                 return;
42061             }
42062         }
42063         this.activePanel = panel;
42064         panel.setActiveState(true);
42065         if(this.panelSize){
42066             panel.setSize(this.panelSize.width, this.panelSize.height);
42067         }
42068         if(this.closeBtn){
42069             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
42070         }
42071         this.updateTitle(panel.getTitle());
42072         if(this.tabs){
42073             this.fireEvent("invalidated", this);
42074         }
42075         this.fireEvent("panelactivated", this, panel);
42076     },
42077
42078     /**
42079      * Shows the specified panel.
42080      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
42081      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
42082      */
42083     showPanel : function(panel)
42084     {
42085         panel = this.getPanel(panel);
42086         if(panel){
42087             if(this.tabs){
42088                 var tab = this.tabs.getTab(panel.getEl().id);
42089                 if(tab.isHidden()){
42090                     this.tabs.unhideTab(tab.id);
42091                 }
42092                 tab.activate();
42093             }else{
42094                 this.setActivePanel(panel);
42095             }
42096         }
42097         return panel;
42098     },
42099
42100     /**
42101      * Get the active panel for this region.
42102      * @return {Roo.ContentPanel} The active panel or null
42103      */
42104     getActivePanel : function(){
42105         return this.activePanel;
42106     },
42107
42108     validateVisibility : function(){
42109         if(this.panels.getCount() < 1){
42110             this.updateTitle("&#160;");
42111             this.closeBtn.hide();
42112             this.hide();
42113         }else{
42114             if(!this.isVisible()){
42115                 this.show();
42116             }
42117         }
42118     },
42119
42120     /**
42121      * Adds the passed ContentPanel(s) to this region.
42122      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42123      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
42124      */
42125     add : function(panel)
42126     {
42127         if(arguments.length > 1){
42128             for(var i = 0, len = arguments.length; i < len; i++) {
42129                 this.add(arguments[i]);
42130             }
42131             return null;
42132         }
42133         
42134         // if we have not been rendered yet, then we can not really do much of this..
42135         if (!this.bodyEl) {
42136             this.unrendered_panels.push(panel);
42137             return panel;
42138         }
42139         
42140         
42141         
42142         
42143         if(this.hasPanel(panel)){
42144             this.showPanel(panel);
42145             return panel;
42146         }
42147         panel.setRegion(this);
42148         this.panels.add(panel);
42149        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
42150             // sinle panel - no tab...?? would it not be better to render it with the tabs,
42151             // and hide them... ???
42152             this.bodyEl.dom.appendChild(panel.getEl().dom);
42153             if(panel.background !== true){
42154                 this.setActivePanel(panel);
42155             }
42156             this.fireEvent("paneladded", this, panel);
42157             return panel;
42158         }
42159         */
42160         if(!this.tabs){
42161             this.initTabs();
42162         }else{
42163             this.initPanelAsTab(panel);
42164         }
42165         
42166         
42167         if(panel.background !== true){
42168             this.tabs.activate(panel.getEl().id);
42169         }
42170         this.fireEvent("paneladded", this, panel);
42171         return panel;
42172     },
42173
42174     /**
42175      * Hides the tab for the specified panel.
42176      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42177      */
42178     hidePanel : function(panel){
42179         if(this.tabs && (panel = this.getPanel(panel))){
42180             this.tabs.hideTab(panel.getEl().id);
42181         }
42182     },
42183
42184     /**
42185      * Unhides the tab for a previously hidden panel.
42186      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42187      */
42188     unhidePanel : function(panel){
42189         if(this.tabs && (panel = this.getPanel(panel))){
42190             this.tabs.unhideTab(panel.getEl().id);
42191         }
42192     },
42193
42194     clearPanels : function(){
42195         while(this.panels.getCount() > 0){
42196              this.remove(this.panels.first());
42197         }
42198     },
42199
42200     /**
42201      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
42202      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42203      * @param {Boolean} preservePanel Overrides the config preservePanel option
42204      * @return {Roo.ContentPanel} The panel that was removed
42205      */
42206     remove : function(panel, preservePanel)
42207     {
42208         panel = this.getPanel(panel);
42209         if(!panel){
42210             return null;
42211         }
42212         var e = {};
42213         this.fireEvent("beforeremove", this, panel, e);
42214         if(e.cancel === true){
42215             return null;
42216         }
42217         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
42218         var panelId = panel.getId();
42219         this.panels.removeKey(panelId);
42220         if(preservePanel){
42221             document.body.appendChild(panel.getEl().dom);
42222         }
42223         if(this.tabs){
42224             this.tabs.removeTab(panel.getEl().id);
42225         }else if (!preservePanel){
42226             this.bodyEl.dom.removeChild(panel.getEl().dom);
42227         }
42228         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
42229             var p = this.panels.first();
42230             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
42231             tempEl.appendChild(p.getEl().dom);
42232             this.bodyEl.update("");
42233             this.bodyEl.dom.appendChild(p.getEl().dom);
42234             tempEl = null;
42235             this.updateTitle(p.getTitle());
42236             this.tabs = null;
42237             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
42238             this.setActivePanel(p);
42239         }
42240         panel.setRegion(null);
42241         if(this.activePanel == panel){
42242             this.activePanel = null;
42243         }
42244         if(this.config.autoDestroy !== false && preservePanel !== true){
42245             try{panel.destroy();}catch(e){}
42246         }
42247         this.fireEvent("panelremoved", this, panel);
42248         return panel;
42249     },
42250
42251     /**
42252      * Returns the TabPanel component used by this region
42253      * @return {Roo.TabPanel}
42254      */
42255     getTabs : function(){
42256         return this.tabs;
42257     },
42258
42259     createTool : function(parentEl, className){
42260         var btn = Roo.DomHelper.append(parentEl, {
42261             tag: "div",
42262             cls: "x-layout-tools-button",
42263             children: [ {
42264                 tag: "div",
42265                 cls: "roo-layout-tools-button-inner " + className,
42266                 html: "&#160;"
42267             }]
42268         }, true);
42269         btn.addClassOnOver("roo-layout-tools-button-over");
42270         return btn;
42271     }
42272 });/*
42273  * Based on:
42274  * Ext JS Library 1.1.1
42275  * Copyright(c) 2006-2007, Ext JS, LLC.
42276  *
42277  * Originally Released Under LGPL - original licence link has changed is not relivant.
42278  *
42279  * Fork - LGPL
42280  * <script type="text/javascript">
42281  */
42282  
42283
42284
42285 /**
42286  * @class Roo.SplitLayoutRegion
42287  * @extends Roo.LayoutRegion
42288  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
42289  */
42290 Roo.bootstrap.layout.Split = function(config){
42291     this.cursor = config.cursor;
42292     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
42293 };
42294
42295 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
42296 {
42297     splitTip : "Drag to resize.",
42298     collapsibleSplitTip : "Drag to resize. Double click to hide.",
42299     useSplitTips : false,
42300
42301     applyConfig : function(config){
42302         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
42303     },
42304     
42305     onRender : function(ctr,pos) {
42306         
42307         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
42308         if(!this.config.split){
42309             return;
42310         }
42311         if(!this.split){
42312             
42313             var splitEl = Roo.DomHelper.append(ctr.dom,  {
42314                             tag: "div",
42315                             id: this.el.id + "-split",
42316                             cls: "roo-layout-split roo-layout-split-"+this.position,
42317                             html: "&#160;"
42318             });
42319             /** The SplitBar for this region 
42320             * @type Roo.SplitBar */
42321             // does not exist yet...
42322             Roo.log([this.position, this.orientation]);
42323             
42324             this.split = new Roo.bootstrap.SplitBar({
42325                 dragElement : splitEl,
42326                 resizingElement: this.el,
42327                 orientation : this.orientation
42328             });
42329             
42330             this.split.on("moved", this.onSplitMove, this);
42331             this.split.useShim = this.config.useShim === true;
42332             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
42333             if(this.useSplitTips){
42334                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
42335             }
42336             //if(config.collapsible){
42337             //    this.split.el.on("dblclick", this.collapse,  this);
42338             //}
42339         }
42340         if(typeof this.config.minSize != "undefined"){
42341             this.split.minSize = this.config.minSize;
42342         }
42343         if(typeof this.config.maxSize != "undefined"){
42344             this.split.maxSize = this.config.maxSize;
42345         }
42346         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
42347             this.hideSplitter();
42348         }
42349         
42350     },
42351
42352     getHMaxSize : function(){
42353          var cmax = this.config.maxSize || 10000;
42354          var center = this.mgr.getRegion("center");
42355          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
42356     },
42357
42358     getVMaxSize : function(){
42359          var cmax = this.config.maxSize || 10000;
42360          var center = this.mgr.getRegion("center");
42361          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
42362     },
42363
42364     onSplitMove : function(split, newSize){
42365         this.fireEvent("resized", this, newSize);
42366     },
42367     
42368     /** 
42369      * Returns the {@link Roo.SplitBar} for this region.
42370      * @return {Roo.SplitBar}
42371      */
42372     getSplitBar : function(){
42373         return this.split;
42374     },
42375     
42376     hide : function(){
42377         this.hideSplitter();
42378         Roo.bootstrap.layout.Split.superclass.hide.call(this);
42379     },
42380
42381     hideSplitter : function(){
42382         if(this.split){
42383             this.split.el.setLocation(-2000,-2000);
42384             this.split.el.hide();
42385         }
42386     },
42387
42388     show : function(){
42389         if(this.split){
42390             this.split.el.show();
42391         }
42392         Roo.bootstrap.layout.Split.superclass.show.call(this);
42393     },
42394     
42395     beforeSlide: function(){
42396         if(Roo.isGecko){// firefox overflow auto bug workaround
42397             this.bodyEl.clip();
42398             if(this.tabs) {
42399                 this.tabs.bodyEl.clip();
42400             }
42401             if(this.activePanel){
42402                 this.activePanel.getEl().clip();
42403                 
42404                 if(this.activePanel.beforeSlide){
42405                     this.activePanel.beforeSlide();
42406                 }
42407             }
42408         }
42409     },
42410     
42411     afterSlide : function(){
42412         if(Roo.isGecko){// firefox overflow auto bug workaround
42413             this.bodyEl.unclip();
42414             if(this.tabs) {
42415                 this.tabs.bodyEl.unclip();
42416             }
42417             if(this.activePanel){
42418                 this.activePanel.getEl().unclip();
42419                 if(this.activePanel.afterSlide){
42420                     this.activePanel.afterSlide();
42421                 }
42422             }
42423         }
42424     },
42425
42426     initAutoHide : function(){
42427         if(this.autoHide !== false){
42428             if(!this.autoHideHd){
42429                 var st = new Roo.util.DelayedTask(this.slideIn, this);
42430                 this.autoHideHd = {
42431                     "mouseout": function(e){
42432                         if(!e.within(this.el, true)){
42433                             st.delay(500);
42434                         }
42435                     },
42436                     "mouseover" : function(e){
42437                         st.cancel();
42438                     },
42439                     scope : this
42440                 };
42441             }
42442             this.el.on(this.autoHideHd);
42443         }
42444     },
42445
42446     clearAutoHide : function(){
42447         if(this.autoHide !== false){
42448             this.el.un("mouseout", this.autoHideHd.mouseout);
42449             this.el.un("mouseover", this.autoHideHd.mouseover);
42450         }
42451     },
42452
42453     clearMonitor : function(){
42454         Roo.get(document).un("click", this.slideInIf, this);
42455     },
42456
42457     // these names are backwards but not changed for compat
42458     slideOut : function(){
42459         if(this.isSlid || this.el.hasActiveFx()){
42460             return;
42461         }
42462         this.isSlid = true;
42463         if(this.collapseBtn){
42464             this.collapseBtn.hide();
42465         }
42466         this.closeBtnState = this.closeBtn.getStyle('display');
42467         this.closeBtn.hide();
42468         if(this.stickBtn){
42469             this.stickBtn.show();
42470         }
42471         this.el.show();
42472         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
42473         this.beforeSlide();
42474         this.el.setStyle("z-index", 10001);
42475         this.el.slideIn(this.getSlideAnchor(), {
42476             callback: function(){
42477                 this.afterSlide();
42478                 this.initAutoHide();
42479                 Roo.get(document).on("click", this.slideInIf, this);
42480                 this.fireEvent("slideshow", this);
42481             },
42482             scope: this,
42483             block: true
42484         });
42485     },
42486
42487     afterSlideIn : function(){
42488         this.clearAutoHide();
42489         this.isSlid = false;
42490         this.clearMonitor();
42491         this.el.setStyle("z-index", "");
42492         if(this.collapseBtn){
42493             this.collapseBtn.show();
42494         }
42495         this.closeBtn.setStyle('display', this.closeBtnState);
42496         if(this.stickBtn){
42497             this.stickBtn.hide();
42498         }
42499         this.fireEvent("slidehide", this);
42500     },
42501
42502     slideIn : function(cb){
42503         if(!this.isSlid || this.el.hasActiveFx()){
42504             Roo.callback(cb);
42505             return;
42506         }
42507         this.isSlid = false;
42508         this.beforeSlide();
42509         this.el.slideOut(this.getSlideAnchor(), {
42510             callback: function(){
42511                 this.el.setLeftTop(-10000, -10000);
42512                 this.afterSlide();
42513                 this.afterSlideIn();
42514                 Roo.callback(cb);
42515             },
42516             scope: this,
42517             block: true
42518         });
42519     },
42520     
42521     slideInIf : function(e){
42522         if(!e.within(this.el)){
42523             this.slideIn();
42524         }
42525     },
42526
42527     animateCollapse : function(){
42528         this.beforeSlide();
42529         this.el.setStyle("z-index", 20000);
42530         var anchor = this.getSlideAnchor();
42531         this.el.slideOut(anchor, {
42532             callback : function(){
42533                 this.el.setStyle("z-index", "");
42534                 this.collapsedEl.slideIn(anchor, {duration:.3});
42535                 this.afterSlide();
42536                 this.el.setLocation(-10000,-10000);
42537                 this.el.hide();
42538                 this.fireEvent("collapsed", this);
42539             },
42540             scope: this,
42541             block: true
42542         });
42543     },
42544
42545     animateExpand : function(){
42546         this.beforeSlide();
42547         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
42548         this.el.setStyle("z-index", 20000);
42549         this.collapsedEl.hide({
42550             duration:.1
42551         });
42552         this.el.slideIn(this.getSlideAnchor(), {
42553             callback : function(){
42554                 this.el.setStyle("z-index", "");
42555                 this.afterSlide();
42556                 if(this.split){
42557                     this.split.el.show();
42558                 }
42559                 this.fireEvent("invalidated", this);
42560                 this.fireEvent("expanded", this);
42561             },
42562             scope: this,
42563             block: true
42564         });
42565     },
42566
42567     anchors : {
42568         "west" : "left",
42569         "east" : "right",
42570         "north" : "top",
42571         "south" : "bottom"
42572     },
42573
42574     sanchors : {
42575         "west" : "l",
42576         "east" : "r",
42577         "north" : "t",
42578         "south" : "b"
42579     },
42580
42581     canchors : {
42582         "west" : "tl-tr",
42583         "east" : "tr-tl",
42584         "north" : "tl-bl",
42585         "south" : "bl-tl"
42586     },
42587
42588     getAnchor : function(){
42589         return this.anchors[this.position];
42590     },
42591
42592     getCollapseAnchor : function(){
42593         return this.canchors[this.position];
42594     },
42595
42596     getSlideAnchor : function(){
42597         return this.sanchors[this.position];
42598     },
42599
42600     getAlignAdj : function(){
42601         var cm = this.cmargins;
42602         switch(this.position){
42603             case "west":
42604                 return [0, 0];
42605             break;
42606             case "east":
42607                 return [0, 0];
42608             break;
42609             case "north":
42610                 return [0, 0];
42611             break;
42612             case "south":
42613                 return [0, 0];
42614             break;
42615         }
42616     },
42617
42618     getExpandAdj : function(){
42619         var c = this.collapsedEl, cm = this.cmargins;
42620         switch(this.position){
42621             case "west":
42622                 return [-(cm.right+c.getWidth()+cm.left), 0];
42623             break;
42624             case "east":
42625                 return [cm.right+c.getWidth()+cm.left, 0];
42626             break;
42627             case "north":
42628                 return [0, -(cm.top+cm.bottom+c.getHeight())];
42629             break;
42630             case "south":
42631                 return [0, cm.top+cm.bottom+c.getHeight()];
42632             break;
42633         }
42634     }
42635 });/*
42636  * Based on:
42637  * Ext JS Library 1.1.1
42638  * Copyright(c) 2006-2007, Ext JS, LLC.
42639  *
42640  * Originally Released Under LGPL - original licence link has changed is not relivant.
42641  *
42642  * Fork - LGPL
42643  * <script type="text/javascript">
42644  */
42645 /*
42646  * These classes are private internal classes
42647  */
42648 Roo.bootstrap.layout.Center = function(config){
42649     config.region = "center";
42650     Roo.bootstrap.layout.Region.call(this, config);
42651     this.visible = true;
42652     this.minWidth = config.minWidth || 20;
42653     this.minHeight = config.minHeight || 20;
42654 };
42655
42656 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
42657     hide : function(){
42658         // center panel can't be hidden
42659     },
42660     
42661     show : function(){
42662         // center panel can't be hidden
42663     },
42664     
42665     getMinWidth: function(){
42666         return this.minWidth;
42667     },
42668     
42669     getMinHeight: function(){
42670         return this.minHeight;
42671     }
42672 });
42673
42674
42675
42676
42677  
42678
42679
42680
42681
42682
42683
42684 Roo.bootstrap.layout.North = function(config)
42685 {
42686     config.region = 'north';
42687     config.cursor = 'n-resize';
42688     
42689     Roo.bootstrap.layout.Split.call(this, config);
42690     
42691     
42692     if(this.split){
42693         this.split.placement = Roo.bootstrap.SplitBar.TOP;
42694         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42695         this.split.el.addClass("roo-layout-split-v");
42696     }
42697     //var size = config.initialSize || config.height;
42698     //if(this.el && typeof size != "undefined"){
42699     //    this.el.setHeight(size);
42700     //}
42701 };
42702 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
42703 {
42704     orientation: Roo.bootstrap.SplitBar.VERTICAL,
42705      
42706      
42707     onRender : function(ctr, pos)
42708     {
42709         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42710         var size = this.config.initialSize || this.config.height;
42711         if(this.el && typeof size != "undefined"){
42712             this.el.setHeight(size);
42713         }
42714     
42715     },
42716     
42717     getBox : function(){
42718         if(this.collapsed){
42719             return this.collapsedEl.getBox();
42720         }
42721         var box = this.el.getBox();
42722         if(this.split){
42723             box.height += this.split.el.getHeight();
42724         }
42725         return box;
42726     },
42727     
42728     updateBox : function(box){
42729         if(this.split && !this.collapsed){
42730             box.height -= this.split.el.getHeight();
42731             this.split.el.setLeft(box.x);
42732             this.split.el.setTop(box.y+box.height);
42733             this.split.el.setWidth(box.width);
42734         }
42735         if(this.collapsed){
42736             this.updateBody(box.width, null);
42737         }
42738         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42739     }
42740 });
42741
42742
42743
42744
42745
42746 Roo.bootstrap.layout.South = function(config){
42747     config.region = 'south';
42748     config.cursor = 's-resize';
42749     Roo.bootstrap.layout.Split.call(this, config);
42750     if(this.split){
42751         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
42752         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42753         this.split.el.addClass("roo-layout-split-v");
42754     }
42755     
42756 };
42757
42758 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
42759     orientation: Roo.bootstrap.SplitBar.VERTICAL,
42760     
42761     onRender : function(ctr, pos)
42762     {
42763         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42764         var size = this.config.initialSize || this.config.height;
42765         if(this.el && typeof size != "undefined"){
42766             this.el.setHeight(size);
42767         }
42768     
42769     },
42770     
42771     getBox : function(){
42772         if(this.collapsed){
42773             return this.collapsedEl.getBox();
42774         }
42775         var box = this.el.getBox();
42776         if(this.split){
42777             var sh = this.split.el.getHeight();
42778             box.height += sh;
42779             box.y -= sh;
42780         }
42781         return box;
42782     },
42783     
42784     updateBox : function(box){
42785         if(this.split && !this.collapsed){
42786             var sh = this.split.el.getHeight();
42787             box.height -= sh;
42788             box.y += sh;
42789             this.split.el.setLeft(box.x);
42790             this.split.el.setTop(box.y-sh);
42791             this.split.el.setWidth(box.width);
42792         }
42793         if(this.collapsed){
42794             this.updateBody(box.width, null);
42795         }
42796         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42797     }
42798 });
42799
42800 Roo.bootstrap.layout.East = function(config){
42801     config.region = "east";
42802     config.cursor = "e-resize";
42803     Roo.bootstrap.layout.Split.call(this, config);
42804     if(this.split){
42805         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
42806         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
42807         this.split.el.addClass("roo-layout-split-h");
42808     }
42809     
42810 };
42811 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
42812     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
42813     
42814     onRender : function(ctr, pos)
42815     {
42816         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42817         var size = this.config.initialSize || this.config.width;
42818         if(this.el && typeof size != "undefined"){
42819             this.el.setWidth(size);
42820         }
42821     
42822     },
42823     
42824     getBox : function(){
42825         if(this.collapsed){
42826             return this.collapsedEl.getBox();
42827         }
42828         var box = this.el.getBox();
42829         if(this.split){
42830             var sw = this.split.el.getWidth();
42831             box.width += sw;
42832             box.x -= sw;
42833         }
42834         return box;
42835     },
42836
42837     updateBox : function(box){
42838         if(this.split && !this.collapsed){
42839             var sw = this.split.el.getWidth();
42840             box.width -= sw;
42841             this.split.el.setLeft(box.x);
42842             this.split.el.setTop(box.y);
42843             this.split.el.setHeight(box.height);
42844             box.x += sw;
42845         }
42846         if(this.collapsed){
42847             this.updateBody(null, box.height);
42848         }
42849         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42850     }
42851 });
42852
42853 Roo.bootstrap.layout.West = function(config){
42854     config.region = "west";
42855     config.cursor = "w-resize";
42856     
42857     Roo.bootstrap.layout.Split.call(this, config);
42858     if(this.split){
42859         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
42860         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
42861         this.split.el.addClass("roo-layout-split-h");
42862     }
42863     
42864 };
42865 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
42866     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
42867     
42868     onRender: function(ctr, pos)
42869     {
42870         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
42871         var size = this.config.initialSize || this.config.width;
42872         if(typeof size != "undefined"){
42873             this.el.setWidth(size);
42874         }
42875     },
42876     
42877     getBox : function(){
42878         if(this.collapsed){
42879             return this.collapsedEl.getBox();
42880         }
42881         var box = this.el.getBox();
42882         if (box.width == 0) {
42883             box.width = this.config.width; // kludge?
42884         }
42885         if(this.split){
42886             box.width += this.split.el.getWidth();
42887         }
42888         return box;
42889     },
42890     
42891     updateBox : function(box){
42892         if(this.split && !this.collapsed){
42893             var sw = this.split.el.getWidth();
42894             box.width -= sw;
42895             this.split.el.setLeft(box.x+box.width);
42896             this.split.el.setTop(box.y);
42897             this.split.el.setHeight(box.height);
42898         }
42899         if(this.collapsed){
42900             this.updateBody(null, box.height);
42901         }
42902         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42903     }
42904 });/*
42905  * Based on:
42906  * Ext JS Library 1.1.1
42907  * Copyright(c) 2006-2007, Ext JS, LLC.
42908  *
42909  * Originally Released Under LGPL - original licence link has changed is not relivant.
42910  *
42911  * Fork - LGPL
42912  * <script type="text/javascript">
42913  */
42914 /**
42915  * @class Roo.bootstrap.paenl.Content
42916  * @extends Roo.util.Observable
42917  * @children Roo.bootstrap.Component
42918  * @parent builder Roo.bootstrap.layout.Border
42919  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
42920  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
42921  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
42922  * @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
42923  * @cfg {Boolean}   closable      True if the panel can be closed/removed
42924  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
42925  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
42926  * @cfg {Toolbar}   toolbar       A toolbar for this panel
42927  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
42928  * @cfg {String} title          The title for this panel
42929  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
42930  * @cfg {String} url            Calls {@link #setUrl} with this value
42931  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
42932  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
42933  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
42934  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
42935  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
42936  * @cfg {Boolean} badges render the badges
42937  * @cfg {String} cls  extra classes to use  
42938  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
42939  
42940  * @constructor
42941  * Create a new ContentPanel.
42942  * @param {String/Object} config A string to set only the title or a config object
42943  
42944  */
42945 Roo.bootstrap.panel.Content = function( config){
42946     
42947     this.tpl = config.tpl || false;
42948     
42949     var el = config.el;
42950     var content = config.content;
42951
42952     if(config.autoCreate){ // xtype is available if this is called from factory
42953         el = Roo.id();
42954     }
42955     this.el = Roo.get(el);
42956     if(!this.el && config && config.autoCreate){
42957         if(typeof config.autoCreate == "object"){
42958             if(!config.autoCreate.id){
42959                 config.autoCreate.id = config.id||el;
42960             }
42961             this.el = Roo.DomHelper.append(document.body,
42962                         config.autoCreate, true);
42963         }else{
42964             var elcfg =  {
42965                 tag: "div",
42966                 cls: (config.cls || '') +
42967                     (config.background ? ' bg-' + config.background : '') +
42968                     " roo-layout-inactive-content",
42969                 id: config.id||el
42970             };
42971             if (config.iframe) {
42972                 elcfg.cn = [
42973                     {
42974                         tag : 'iframe',
42975                         style : 'border: 0px',
42976                         src : 'about:blank'
42977                     }
42978                 ];
42979             }
42980               
42981             if (config.html) {
42982                 elcfg.html = config.html;
42983                 
42984             }
42985                         
42986             this.el = Roo.DomHelper.append(document.body, elcfg , true);
42987             if (config.iframe) {
42988                 this.iframeEl = this.el.select('iframe',true).first();
42989             }
42990             
42991         }
42992     } 
42993     this.closable = false;
42994     this.loaded = false;
42995     this.active = false;
42996    
42997       
42998     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
42999         
43000         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
43001         
43002         this.wrapEl = this.el; //this.el.wrap();
43003         var ti = [];
43004         if (config.toolbar.items) {
43005             ti = config.toolbar.items ;
43006             delete config.toolbar.items ;
43007         }
43008         
43009         var nitems = [];
43010         this.toolbar.render(this.wrapEl, 'before');
43011         for(var i =0;i < ti.length;i++) {
43012           //  Roo.log(['add child', items[i]]);
43013             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43014         }
43015         this.toolbar.items = nitems;
43016         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
43017         delete config.toolbar;
43018         
43019     }
43020     /*
43021     // xtype created footer. - not sure if will work as we normally have to render first..
43022     if (this.footer && !this.footer.el && this.footer.xtype) {
43023         if (!this.wrapEl) {
43024             this.wrapEl = this.el.wrap();
43025         }
43026     
43027         this.footer.container = this.wrapEl.createChild();
43028          
43029         this.footer = Roo.factory(this.footer, Roo);
43030         
43031     }
43032     */
43033     
43034      if(typeof config == "string"){
43035         this.title = config;
43036     }else{
43037         Roo.apply(this, config);
43038     }
43039     
43040     if(this.resizeEl){
43041         this.resizeEl = Roo.get(this.resizeEl, true);
43042     }else{
43043         this.resizeEl = this.el;
43044     }
43045     // handle view.xtype
43046     
43047  
43048     
43049     
43050     this.addEvents({
43051         /**
43052          * @event activate
43053          * Fires when this panel is activated. 
43054          * @param {Roo.ContentPanel} this
43055          */
43056         "activate" : true,
43057         /**
43058          * @event deactivate
43059          * Fires when this panel is activated. 
43060          * @param {Roo.ContentPanel} this
43061          */
43062         "deactivate" : true,
43063
43064         /**
43065          * @event resize
43066          * Fires when this panel is resized if fitToFrame is true.
43067          * @param {Roo.ContentPanel} this
43068          * @param {Number} width The width after any component adjustments
43069          * @param {Number} height The height after any component adjustments
43070          */
43071         "resize" : true,
43072         
43073          /**
43074          * @event render
43075          * Fires when this tab is created
43076          * @param {Roo.ContentPanel} this
43077          */
43078         "render" : true,
43079         
43080           /**
43081          * @event scroll
43082          * Fires when this content is scrolled
43083          * @param {Roo.ContentPanel} this
43084          * @param {Event} scrollEvent
43085          */
43086         "scroll" : true
43087         
43088         
43089         
43090     });
43091     
43092
43093     
43094     
43095     if(this.autoScroll && !this.iframe){
43096         this.resizeEl.setStyle("overflow", "auto");
43097         this.resizeEl.on('scroll', this.onScroll, this);
43098     } else {
43099         // fix randome scrolling
43100         //this.el.on('scroll', function() {
43101         //    Roo.log('fix random scolling');
43102         //    this.scrollTo('top',0); 
43103         //});
43104     }
43105     content = content || this.content;
43106     if(content){
43107         this.setContent(content);
43108     }
43109     if(config && config.url){
43110         this.setUrl(this.url, this.params, this.loadOnce);
43111     }
43112     
43113     
43114     
43115     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
43116     
43117     if (this.view && typeof(this.view.xtype) != 'undefined') {
43118         this.view.el = this.el.appendChild(document.createElement("div"));
43119         this.view = Roo.factory(this.view); 
43120         this.view.render  &&  this.view.render(false, '');  
43121     }
43122     
43123     
43124     this.fireEvent('render', this);
43125 };
43126
43127 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
43128     
43129     cls : '',
43130     background : '',
43131     
43132     tabTip : '',
43133     
43134     iframe : false,
43135     iframeEl : false,
43136     
43137     /* Resize Element - use this to work out scroll etc. */
43138     resizeEl : false,
43139     
43140     setRegion : function(region){
43141         this.region = region;
43142         this.setActiveClass(region && !this.background);
43143     },
43144     
43145     
43146     setActiveClass: function(state)
43147     {
43148         if(state){
43149            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
43150            this.el.setStyle('position','relative');
43151         }else{
43152            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
43153            this.el.setStyle('position', 'absolute');
43154         } 
43155     },
43156     
43157     /**
43158      * Returns the toolbar for this Panel if one was configured. 
43159      * @return {Roo.Toolbar} 
43160      */
43161     getToolbar : function(){
43162         return this.toolbar;
43163     },
43164     
43165     setActiveState : function(active)
43166     {
43167         this.active = active;
43168         this.setActiveClass(active);
43169         if(!active){
43170             if(this.fireEvent("deactivate", this) === false){
43171                 return false;
43172             }
43173             return true;
43174         }
43175         this.fireEvent("activate", this);
43176         return true;
43177     },
43178     /**
43179      * Updates this panel's element (not for iframe)
43180      * @param {String} content The new content
43181      * @param {Boolean} loadScripts (optional) true to look for and process scripts
43182     */
43183     setContent : function(content, loadScripts){
43184         if (this.iframe) {
43185             return;
43186         }
43187         
43188         this.el.update(content, loadScripts);
43189     },
43190
43191     ignoreResize : function(w, h)
43192     {
43193         //return false; // always resize?
43194         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
43195             return true;
43196         }else{
43197             this.lastSize = {width: w, height: h};
43198             return false;
43199         }
43200     },
43201     /**
43202      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
43203      * @return {Roo.UpdateManager} The UpdateManager
43204      */
43205     getUpdateManager : function(){
43206         if (this.iframe) {
43207             return false;
43208         }
43209         return this.el.getUpdateManager();
43210     },
43211      /**
43212      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
43213      * Does not work with IFRAME contents
43214      * @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:
43215 <pre><code>
43216 panel.load({
43217     url: "your-url.php",
43218     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
43219     callback: yourFunction,
43220     scope: yourObject, //(optional scope)
43221     discardUrl: false,
43222     nocache: false,
43223     text: "Loading...",
43224     timeout: 30,
43225     scripts: false
43226 });
43227 </code></pre>
43228      
43229      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
43230      * 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.
43231      * @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}
43232      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
43233      * @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.
43234      * @return {Roo.ContentPanel} this
43235      */
43236     load : function(){
43237         
43238         if (this.iframe) {
43239             return this;
43240         }
43241         
43242         var um = this.el.getUpdateManager();
43243         um.update.apply(um, arguments);
43244         return this;
43245     },
43246
43247
43248     /**
43249      * 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.
43250      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
43251      * @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)
43252      * @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)
43253      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
43254      */
43255     setUrl : function(url, params, loadOnce){
43256         if (this.iframe) {
43257             this.iframeEl.dom.src = url;
43258             return false;
43259         }
43260         
43261         if(this.refreshDelegate){
43262             this.removeListener("activate", this.refreshDelegate);
43263         }
43264         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
43265         this.on("activate", this.refreshDelegate);
43266         return this.el.getUpdateManager();
43267     },
43268     
43269     _handleRefresh : function(url, params, loadOnce){
43270         if(!loadOnce || !this.loaded){
43271             var updater = this.el.getUpdateManager();
43272             updater.update(url, params, this._setLoaded.createDelegate(this));
43273         }
43274     },
43275     
43276     _setLoaded : function(){
43277         this.loaded = true;
43278     }, 
43279     
43280     /**
43281      * Returns this panel's id
43282      * @return {String} 
43283      */
43284     getId : function(){
43285         return this.el.id;
43286     },
43287     
43288     /** 
43289      * Returns this panel's element - used by regiosn to add.
43290      * @return {Roo.Element} 
43291      */
43292     getEl : function(){
43293         return this.wrapEl || this.el;
43294     },
43295     
43296    
43297     
43298     adjustForComponents : function(width, height)
43299     {
43300         //Roo.log('adjustForComponents ');
43301         if(this.resizeEl != this.el){
43302             width -= this.el.getFrameWidth('lr');
43303             height -= this.el.getFrameWidth('tb');
43304         }
43305         if(this.toolbar){
43306             var te = this.toolbar.getEl();
43307             te.setWidth(width);
43308             height -= te.getHeight();
43309         }
43310         if(this.footer){
43311             var te = this.footer.getEl();
43312             te.setWidth(width);
43313             height -= te.getHeight();
43314         }
43315         
43316         
43317         if(this.adjustments){
43318             width += this.adjustments[0];
43319             height += this.adjustments[1];
43320         }
43321         return {"width": width, "height": height};
43322     },
43323     
43324     setSize : function(width, height){
43325         if(this.fitToFrame && !this.ignoreResize(width, height)){
43326             if(this.fitContainer && this.resizeEl != this.el){
43327                 this.el.setSize(width, height);
43328             }
43329             var size = this.adjustForComponents(width, height);
43330             if (this.iframe) {
43331                 this.iframeEl.setSize(width,height);
43332             }
43333             
43334             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
43335             this.fireEvent('resize', this, size.width, size.height);
43336             
43337             
43338         }
43339     },
43340     
43341     /**
43342      * Returns this panel's title
43343      * @return {String} 
43344      */
43345     getTitle : function(){
43346         
43347         if (typeof(this.title) != 'object') {
43348             return this.title;
43349         }
43350         
43351         var t = '';
43352         for (var k in this.title) {
43353             if (!this.title.hasOwnProperty(k)) {
43354                 continue;
43355             }
43356             
43357             if (k.indexOf('-') >= 0) {
43358                 var s = k.split('-');
43359                 for (var i = 0; i<s.length; i++) {
43360                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
43361                 }
43362             } else {
43363                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
43364             }
43365         }
43366         return t;
43367     },
43368     
43369     /**
43370      * Set this panel's title
43371      * @param {String} title
43372      */
43373     setTitle : function(title){
43374         this.title = title;
43375         if(this.region){
43376             this.region.updatePanelTitle(this, title);
43377         }
43378     },
43379     
43380     /**
43381      * Returns true is this panel was configured to be closable
43382      * @return {Boolean} 
43383      */
43384     isClosable : function(){
43385         return this.closable;
43386     },
43387     
43388     beforeSlide : function(){
43389         this.el.clip();
43390         this.resizeEl.clip();
43391     },
43392     
43393     afterSlide : function(){
43394         this.el.unclip();
43395         this.resizeEl.unclip();
43396     },
43397     
43398     /**
43399      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
43400      *   Will fail silently if the {@link #setUrl} method has not been called.
43401      *   This does not activate the panel, just updates its content.
43402      */
43403     refresh : function(){
43404         if(this.refreshDelegate){
43405            this.loaded = false;
43406            this.refreshDelegate();
43407         }
43408     },
43409     
43410     /**
43411      * Destroys this panel
43412      */
43413     destroy : function(){
43414         this.el.removeAllListeners();
43415         var tempEl = document.createElement("span");
43416         tempEl.appendChild(this.el.dom);
43417         tempEl.innerHTML = "";
43418         this.el.remove();
43419         this.el = null;
43420     },
43421     
43422     /**
43423      * form - if the content panel contains a form - this is a reference to it.
43424      * @type {Roo.form.Form}
43425      */
43426     form : false,
43427     /**
43428      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
43429      *    This contains a reference to it.
43430      * @type {Roo.View}
43431      */
43432     view : false,
43433     
43434       /**
43435      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
43436      * <pre><code>
43437
43438 layout.addxtype({
43439        xtype : 'Form',
43440        items: [ .... ]
43441    }
43442 );
43443
43444 </code></pre>
43445      * @param {Object} cfg Xtype definition of item to add.
43446      */
43447     
43448     
43449     getChildContainer: function () {
43450         return this.getEl();
43451     },
43452     
43453     
43454     onScroll : function(e)
43455     {
43456         this.fireEvent('scroll', this, e);
43457     }
43458     
43459     
43460     /*
43461         var  ret = new Roo.factory(cfg);
43462         return ret;
43463         
43464         
43465         // add form..
43466         if (cfg.xtype.match(/^Form$/)) {
43467             
43468             var el;
43469             //if (this.footer) {
43470             //    el = this.footer.container.insertSibling(false, 'before');
43471             //} else {
43472                 el = this.el.createChild();
43473             //}
43474
43475             this.form = new  Roo.form.Form(cfg);
43476             
43477             
43478             if ( this.form.allItems.length) {
43479                 this.form.render(el.dom);
43480             }
43481             return this.form;
43482         }
43483         // should only have one of theses..
43484         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
43485             // views.. should not be just added - used named prop 'view''
43486             
43487             cfg.el = this.el.appendChild(document.createElement("div"));
43488             // factory?
43489             
43490             var ret = new Roo.factory(cfg);
43491              
43492              ret.render && ret.render(false, ''); // render blank..
43493             this.view = ret;
43494             return ret;
43495         }
43496         return false;
43497     }
43498     \*/
43499 });
43500  
43501 /**
43502  * @class Roo.bootstrap.panel.Grid
43503  * @extends Roo.bootstrap.panel.Content
43504  * @constructor
43505  * Create a new GridPanel.
43506  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
43507  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
43508  * @param {Object} config A the config object
43509   
43510  */
43511
43512
43513
43514 Roo.bootstrap.panel.Grid = function(config)
43515 {
43516     
43517       
43518     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
43519         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
43520
43521     config.el = this.wrapper;
43522     //this.el = this.wrapper;
43523     
43524       if (config.container) {
43525         // ctor'ed from a Border/panel.grid
43526         
43527         
43528         this.wrapper.setStyle("overflow", "hidden");
43529         this.wrapper.addClass('roo-grid-container');
43530
43531     }
43532     
43533     
43534     if(config.toolbar){
43535         var tool_el = this.wrapper.createChild();    
43536         this.toolbar = Roo.factory(config.toolbar);
43537         var ti = [];
43538         if (config.toolbar.items) {
43539             ti = config.toolbar.items ;
43540             delete config.toolbar.items ;
43541         }
43542         
43543         var nitems = [];
43544         this.toolbar.render(tool_el);
43545         for(var i =0;i < ti.length;i++) {
43546           //  Roo.log(['add child', items[i]]);
43547             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43548         }
43549         this.toolbar.items = nitems;
43550         
43551         delete config.toolbar;
43552     }
43553     
43554     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
43555     config.grid.scrollBody = true;;
43556     config.grid.monitorWindowResize = false; // turn off autosizing
43557     config.grid.autoHeight = false;
43558     config.grid.autoWidth = false;
43559     
43560     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
43561     
43562     if (config.background) {
43563         // render grid on panel activation (if panel background)
43564         this.on('activate', function(gp) {
43565             if (!gp.grid.rendered) {
43566                 gp.grid.render(this.wrapper);
43567                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
43568             }
43569         });
43570             
43571     } else {
43572         this.grid.render(this.wrapper);
43573         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
43574
43575     }
43576     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
43577     // ??? needed ??? config.el = this.wrapper;
43578     
43579     
43580     
43581   
43582     // xtype created footer. - not sure if will work as we normally have to render first..
43583     if (this.footer && !this.footer.el && this.footer.xtype) {
43584         
43585         var ctr = this.grid.getView().getFooterPanel(true);
43586         this.footer.dataSource = this.grid.dataSource;
43587         this.footer = Roo.factory(this.footer, Roo);
43588         this.footer.render(ctr);
43589         
43590     }
43591     
43592     
43593     
43594     
43595      
43596 };
43597
43598 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
43599 {
43600   
43601     getId : function(){
43602         return this.grid.id;
43603     },
43604     
43605     /**
43606      * Returns the grid for this panel
43607      * @return {Roo.bootstrap.Table} 
43608      */
43609     getGrid : function(){
43610         return this.grid;    
43611     },
43612     
43613     setSize : function(width, height)
43614     {
43615      
43616         //if(!this.ignoreResize(width, height)){
43617             var grid = this.grid;
43618             var size = this.adjustForComponents(width, height);
43619             // tfoot is not a footer?
43620           
43621             
43622             var gridel = grid.getGridEl();
43623             gridel.setSize(size.width, size.height);
43624             
43625             var tbd = grid.getGridEl().select('tbody', true).first();
43626             var thd = grid.getGridEl().select('thead',true).first();
43627             var tbf= grid.getGridEl().select('tfoot', true).first();
43628
43629             if (tbf) {
43630                 size.height -= tbf.getHeight();
43631             }
43632             if (thd) {
43633                 size.height -= thd.getHeight();
43634             }
43635             
43636             tbd.setSize(size.width, size.height );
43637             // this is for the account management tab -seems to work there.
43638             var thd = grid.getGridEl().select('thead',true).first();
43639             //if (tbd) {
43640             //    tbd.setSize(size.width, size.height - thd.getHeight());
43641             //}
43642              
43643             grid.autoSize();
43644         //}
43645    
43646     },
43647      
43648     
43649     
43650     beforeSlide : function(){
43651         this.grid.getView().scroller.clip();
43652     },
43653     
43654     afterSlide : function(){
43655         this.grid.getView().scroller.unclip();
43656     },
43657     
43658     destroy : function(){
43659         this.grid.destroy();
43660         delete this.grid;
43661         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
43662     }
43663 });
43664
43665 /**
43666  * @class Roo.bootstrap.panel.Nest
43667  * @extends Roo.bootstrap.panel.Content
43668  * @constructor
43669  * Create a new Panel, that can contain a layout.Border.
43670  * 
43671  * 
43672  * @param {String/Object} config A string to set only the title or a config object
43673  */
43674 Roo.bootstrap.panel.Nest = function(config)
43675 {
43676     // construct with only one argument..
43677     /* FIXME - implement nicer consturctors
43678     if (layout.layout) {
43679         config = layout;
43680         layout = config.layout;
43681         delete config.layout;
43682     }
43683     if (layout.xtype && !layout.getEl) {
43684         // then layout needs constructing..
43685         layout = Roo.factory(layout, Roo);
43686     }
43687     */
43688     
43689     config.el =  config.layout.getEl();
43690     
43691     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
43692     
43693     config.layout.monitorWindowResize = false; // turn off autosizing
43694     this.layout = config.layout;
43695     this.layout.getEl().addClass("roo-layout-nested-layout");
43696     this.layout.parent = this;
43697     
43698     
43699     
43700     
43701 };
43702
43703 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
43704     /**
43705     * @cfg {Roo.BorderLayout} layout The layout for this panel
43706     */
43707     layout : false,
43708
43709     setSize : function(width, height){
43710         if(!this.ignoreResize(width, height)){
43711             var size = this.adjustForComponents(width, height);
43712             var el = this.layout.getEl();
43713             if (size.height < 1) {
43714                 el.setWidth(size.width);   
43715             } else {
43716                 el.setSize(size.width, size.height);
43717             }
43718             var touch = el.dom.offsetWidth;
43719             this.layout.layout();
43720             // ie requires a double layout on the first pass
43721             if(Roo.isIE && !this.initialized){
43722                 this.initialized = true;
43723                 this.layout.layout();
43724             }
43725         }
43726     },
43727     
43728     // activate all subpanels if not currently active..
43729     
43730     setActiveState : function(active){
43731         this.active = active;
43732         this.setActiveClass(active);
43733         
43734         if(!active){
43735             this.fireEvent("deactivate", this);
43736             return;
43737         }
43738         
43739         this.fireEvent("activate", this);
43740         // not sure if this should happen before or after..
43741         if (!this.layout) {
43742             return; // should not happen..
43743         }
43744         var reg = false;
43745         for (var r in this.layout.regions) {
43746             reg = this.layout.getRegion(r);
43747             if (reg.getActivePanel()) {
43748                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
43749                 reg.setActivePanel(reg.getActivePanel());
43750                 continue;
43751             }
43752             if (!reg.panels.length) {
43753                 continue;
43754             }
43755             reg.showPanel(reg.getPanel(0));
43756         }
43757         
43758         
43759         
43760         
43761     },
43762     
43763     /**
43764      * Returns the nested BorderLayout for this panel
43765      * @return {Roo.BorderLayout} 
43766      */
43767     getLayout : function(){
43768         return this.layout;
43769     },
43770     
43771      /**
43772      * Adds a xtype elements to the layout of the nested panel
43773      * <pre><code>
43774
43775 panel.addxtype({
43776        xtype : 'ContentPanel',
43777        region: 'west',
43778        items: [ .... ]
43779    }
43780 );
43781
43782 panel.addxtype({
43783         xtype : 'NestedLayoutPanel',
43784         region: 'west',
43785         layout: {
43786            center: { },
43787            west: { }   
43788         },
43789         items : [ ... list of content panels or nested layout panels.. ]
43790    }
43791 );
43792 </code></pre>
43793      * @param {Object} cfg Xtype definition of item to add.
43794      */
43795     addxtype : function(cfg) {
43796         return this.layout.addxtype(cfg);
43797     
43798     }
43799 });/*
43800  * Based on:
43801  * Ext JS Library 1.1.1
43802  * Copyright(c) 2006-2007, Ext JS, LLC.
43803  *
43804  * Originally Released Under LGPL - original licence link has changed is not relivant.
43805  *
43806  * Fork - LGPL
43807  * <script type="text/javascript">
43808  */
43809 /**
43810  * @class Roo.TabPanel
43811  * @extends Roo.util.Observable
43812  * A lightweight tab container.
43813  * <br><br>
43814  * Usage:
43815  * <pre><code>
43816 // basic tabs 1, built from existing content
43817 var tabs = new Roo.TabPanel("tabs1");
43818 tabs.addTab("script", "View Script");
43819 tabs.addTab("markup", "View Markup");
43820 tabs.activate("script");
43821
43822 // more advanced tabs, built from javascript
43823 var jtabs = new Roo.TabPanel("jtabs");
43824 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
43825
43826 // set up the UpdateManager
43827 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
43828 var updater = tab2.getUpdateManager();
43829 updater.setDefaultUrl("ajax1.htm");
43830 tab2.on('activate', updater.refresh, updater, true);
43831
43832 // Use setUrl for Ajax loading
43833 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
43834 tab3.setUrl("ajax2.htm", null, true);
43835
43836 // Disabled tab
43837 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
43838 tab4.disable();
43839
43840 jtabs.activate("jtabs-1");
43841  * </code></pre>
43842  * @constructor
43843  * Create a new TabPanel.
43844  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
43845  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
43846  */
43847 Roo.bootstrap.panel.Tabs = function(config){
43848     /**
43849     * The container element for this TabPanel.
43850     * @type Roo.Element
43851     */
43852     this.el = Roo.get(config.el);
43853     delete config.el;
43854     if(config){
43855         if(typeof config == "boolean"){
43856             this.tabPosition = config ? "bottom" : "top";
43857         }else{
43858             Roo.apply(this, config);
43859         }
43860     }
43861     
43862     if(this.tabPosition == "bottom"){
43863         // if tabs are at the bottom = create the body first.
43864         this.bodyEl = Roo.get(this.createBody(this.el.dom));
43865         this.el.addClass("roo-tabs-bottom");
43866     }
43867     // next create the tabs holders
43868     
43869     if (this.tabPosition == "west"){
43870         
43871         var reg = this.region; // fake it..
43872         while (reg) {
43873             if (!reg.mgr.parent) {
43874                 break;
43875             }
43876             reg = reg.mgr.parent.region;
43877         }
43878         Roo.log("got nest?");
43879         Roo.log(reg);
43880         if (reg.mgr.getRegion('west')) {
43881             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
43882             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
43883             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
43884             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
43885             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
43886         
43887             
43888         }
43889         
43890         
43891     } else {
43892      
43893         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
43894         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
43895         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
43896         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
43897     }
43898     
43899     
43900     if(Roo.isIE){
43901         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
43902     }
43903     
43904     // finally - if tabs are at the top, then create the body last..
43905     if(this.tabPosition != "bottom"){
43906         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
43907          * @type Roo.Element
43908          */
43909         this.bodyEl = Roo.get(this.createBody(this.el.dom));
43910         this.el.addClass("roo-tabs-top");
43911     }
43912     this.items = [];
43913
43914     this.bodyEl.setStyle("position", "relative");
43915
43916     this.active = null;
43917     this.activateDelegate = this.activate.createDelegate(this);
43918
43919     this.addEvents({
43920         /**
43921          * @event tabchange
43922          * Fires when the active tab changes
43923          * @param {Roo.TabPanel} this
43924          * @param {Roo.TabPanelItem} activePanel The new active tab
43925          */
43926         "tabchange": true,
43927         /**
43928          * @event beforetabchange
43929          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
43930          * @param {Roo.TabPanel} this
43931          * @param {Object} e Set cancel to true on this object to cancel the tab change
43932          * @param {Roo.TabPanelItem} tab The tab being changed to
43933          */
43934         "beforetabchange" : true
43935     });
43936
43937     Roo.EventManager.onWindowResize(this.onResize, this);
43938     this.cpad = this.el.getPadding("lr");
43939     this.hiddenCount = 0;
43940
43941
43942     // toolbar on the tabbar support...
43943     if (this.toolbar) {
43944         alert("no toolbar support yet");
43945         this.toolbar  = false;
43946         /*
43947         var tcfg = this.toolbar;
43948         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
43949         this.toolbar = new Roo.Toolbar(tcfg);
43950         if (Roo.isSafari) {
43951             var tbl = tcfg.container.child('table', true);
43952             tbl.setAttribute('width', '100%');
43953         }
43954         */
43955         
43956     }
43957    
43958
43959
43960     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
43961 };
43962
43963 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
43964     /*
43965      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
43966      */
43967     tabPosition : "top",
43968     /*
43969      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
43970      */
43971     currentTabWidth : 0,
43972     /*
43973      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
43974      */
43975     minTabWidth : 40,
43976     /*
43977      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
43978      */
43979     maxTabWidth : 250,
43980     /*
43981      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
43982      */
43983     preferredTabWidth : 175,
43984     /*
43985      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
43986      */
43987     resizeTabs : false,
43988     /*
43989      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
43990      */
43991     monitorResize : true,
43992     /*
43993      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
43994      */
43995     toolbar : false,  // set by caller..
43996     
43997     region : false, /// set by caller
43998     
43999     disableTooltips : true, // not used yet...
44000
44001     /**
44002      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
44003      * @param {String} id The id of the div to use <b>or create</b>
44004      * @param {String} text The text for the tab
44005      * @param {String} content (optional) Content to put in the TabPanelItem body
44006      * @param {Boolean} closable (optional) True to create a close icon on the tab
44007      * @return {Roo.TabPanelItem} The created TabPanelItem
44008      */
44009     addTab : function(id, text, content, closable, tpl)
44010     {
44011         var item = new Roo.bootstrap.panel.TabItem({
44012             panel: this,
44013             id : id,
44014             text : text,
44015             closable : closable,
44016             tpl : tpl
44017         });
44018         this.addTabItem(item);
44019         if(content){
44020             item.setContent(content);
44021         }
44022         return item;
44023     },
44024
44025     /**
44026      * Returns the {@link Roo.TabPanelItem} with the specified id/index
44027      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
44028      * @return {Roo.TabPanelItem}
44029      */
44030     getTab : function(id){
44031         return this.items[id];
44032     },
44033
44034     /**
44035      * Hides the {@link Roo.TabPanelItem} with the specified id/index
44036      * @param {String/Number} id The id or index of the TabPanelItem to hide.
44037      */
44038     hideTab : function(id){
44039         var t = this.items[id];
44040         if(!t.isHidden()){
44041            t.setHidden(true);
44042            this.hiddenCount++;
44043            this.autoSizeTabs();
44044         }
44045     },
44046
44047     /**
44048      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
44049      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
44050      */
44051     unhideTab : function(id){
44052         var t = this.items[id];
44053         if(t.isHidden()){
44054            t.setHidden(false);
44055            this.hiddenCount--;
44056            this.autoSizeTabs();
44057         }
44058     },
44059
44060     /**
44061      * Adds an existing {@link Roo.TabPanelItem}.
44062      * @param {Roo.TabPanelItem} item The TabPanelItem to add
44063      */
44064     addTabItem : function(item)
44065     {
44066         this.items[item.id] = item;
44067         this.items.push(item);
44068         this.autoSizeTabs();
44069       //  if(this.resizeTabs){
44070     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
44071   //         this.autoSizeTabs();
44072 //        }else{
44073 //            item.autoSize();
44074        // }
44075     },
44076
44077     /**
44078      * Removes a {@link Roo.TabPanelItem}.
44079      * @param {String/Number} id The id or index of the TabPanelItem to remove.
44080      */
44081     removeTab : function(id){
44082         var items = this.items;
44083         var tab = items[id];
44084         if(!tab) { return; }
44085         var index = items.indexOf(tab);
44086         if(this.active == tab && items.length > 1){
44087             var newTab = this.getNextAvailable(index);
44088             if(newTab) {
44089                 newTab.activate();
44090             }
44091         }
44092         this.stripEl.dom.removeChild(tab.pnode.dom);
44093         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
44094             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
44095         }
44096         items.splice(index, 1);
44097         delete this.items[tab.id];
44098         tab.fireEvent("close", tab);
44099         tab.purgeListeners();
44100         this.autoSizeTabs();
44101     },
44102
44103     getNextAvailable : function(start){
44104         var items = this.items;
44105         var index = start;
44106         // look for a next tab that will slide over to
44107         // replace the one being removed
44108         while(index < items.length){
44109             var item = items[++index];
44110             if(item && !item.isHidden()){
44111                 return item;
44112             }
44113         }
44114         // if one isn't found select the previous tab (on the left)
44115         index = start;
44116         while(index >= 0){
44117             var item = items[--index];
44118             if(item && !item.isHidden()){
44119                 return item;
44120             }
44121         }
44122         return null;
44123     },
44124
44125     /**
44126      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
44127      * @param {String/Number} id The id or index of the TabPanelItem to disable.
44128      */
44129     disableTab : function(id){
44130         var tab = this.items[id];
44131         if(tab && this.active != tab){
44132             tab.disable();
44133         }
44134     },
44135
44136     /**
44137      * Enables a {@link Roo.TabPanelItem} that is disabled.
44138      * @param {String/Number} id The id or index of the TabPanelItem to enable.
44139      */
44140     enableTab : function(id){
44141         var tab = this.items[id];
44142         tab.enable();
44143     },
44144
44145     /**
44146      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
44147      * @param {String/Number} id The id or index of the TabPanelItem to activate.
44148      * @return {Roo.TabPanelItem} The TabPanelItem.
44149      */
44150     activate : function(id)
44151     {
44152         //Roo.log('activite:'  + id);
44153         
44154         var tab = this.items[id];
44155         if(!tab){
44156             return null;
44157         }
44158         if(tab == this.active || tab.disabled){
44159             return tab;
44160         }
44161         var e = {};
44162         this.fireEvent("beforetabchange", this, e, tab);
44163         if(e.cancel !== true && !tab.disabled){
44164             if(this.active){
44165                 this.active.hide();
44166             }
44167             this.active = this.items[id];
44168             this.active.show();
44169             this.fireEvent("tabchange", this, this.active);
44170         }
44171         return tab;
44172     },
44173
44174     /**
44175      * Gets the active {@link Roo.TabPanelItem}.
44176      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
44177      */
44178     getActiveTab : function(){
44179         return this.active;
44180     },
44181
44182     /**
44183      * Updates the tab body element to fit the height of the container element
44184      * for overflow scrolling
44185      * @param {Number} targetHeight (optional) Override the starting height from the elements height
44186      */
44187     syncHeight : function(targetHeight){
44188         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44189         var bm = this.bodyEl.getMargins();
44190         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
44191         this.bodyEl.setHeight(newHeight);
44192         return newHeight;
44193     },
44194
44195     onResize : function(){
44196         if(this.monitorResize){
44197             this.autoSizeTabs();
44198         }
44199     },
44200
44201     /**
44202      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
44203      */
44204     beginUpdate : function(){
44205         this.updating = true;
44206     },
44207
44208     /**
44209      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
44210      */
44211     endUpdate : function(){
44212         this.updating = false;
44213         this.autoSizeTabs();
44214     },
44215
44216     /**
44217      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
44218      */
44219     autoSizeTabs : function()
44220     {
44221         var count = this.items.length;
44222         var vcount = count - this.hiddenCount;
44223         
44224         if (vcount < 2) {
44225             this.stripEl.hide();
44226         } else {
44227             this.stripEl.show();
44228         }
44229         
44230         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
44231             return;
44232         }
44233         
44234         
44235         var w = Math.max(this.el.getWidth() - this.cpad, 10);
44236         var availWidth = Math.floor(w / vcount);
44237         var b = this.stripBody;
44238         if(b.getWidth() > w){
44239             var tabs = this.items;
44240             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
44241             if(availWidth < this.minTabWidth){
44242                 /*if(!this.sleft){    // incomplete scrolling code
44243                     this.createScrollButtons();
44244                 }
44245                 this.showScroll();
44246                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
44247             }
44248         }else{
44249             if(this.currentTabWidth < this.preferredTabWidth){
44250                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
44251             }
44252         }
44253     },
44254
44255     /**
44256      * Returns the number of tabs in this TabPanel.
44257      * @return {Number}
44258      */
44259      getCount : function(){
44260          return this.items.length;
44261      },
44262
44263     /**
44264      * Resizes all the tabs to the passed width
44265      * @param {Number} The new width
44266      */
44267     setTabWidth : function(width){
44268         this.currentTabWidth = width;
44269         for(var i = 0, len = this.items.length; i < len; i++) {
44270                 if(!this.items[i].isHidden()) {
44271                 this.items[i].setWidth(width);
44272             }
44273         }
44274     },
44275
44276     /**
44277      * Destroys this TabPanel
44278      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
44279      */
44280     destroy : function(removeEl){
44281         Roo.EventManager.removeResizeListener(this.onResize, this);
44282         for(var i = 0, len = this.items.length; i < len; i++){
44283             this.items[i].purgeListeners();
44284         }
44285         if(removeEl === true){
44286             this.el.update("");
44287             this.el.remove();
44288         }
44289     },
44290     
44291     createStrip : function(container)
44292     {
44293         var strip = document.createElement("nav");
44294         strip.className = Roo.bootstrap.version == 4 ?
44295             "navbar-light bg-light" : 
44296             "navbar navbar-default"; //"x-tabs-wrap";
44297         container.appendChild(strip);
44298         return strip;
44299     },
44300     
44301     createStripList : function(strip)
44302     {
44303         // div wrapper for retard IE
44304         // returns the "tr" element.
44305         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
44306         //'<div class="x-tabs-strip-wrap">'+
44307           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
44308           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
44309         return strip.firstChild; //.firstChild.firstChild.firstChild;
44310     },
44311     createBody : function(container)
44312     {
44313         var body = document.createElement("div");
44314         Roo.id(body, "tab-body");
44315         //Roo.fly(body).addClass("x-tabs-body");
44316         Roo.fly(body).addClass("tab-content");
44317         container.appendChild(body);
44318         return body;
44319     },
44320     createItemBody :function(bodyEl, id){
44321         var body = Roo.getDom(id);
44322         if(!body){
44323             body = document.createElement("div");
44324             body.id = id;
44325         }
44326         //Roo.fly(body).addClass("x-tabs-item-body");
44327         Roo.fly(body).addClass("tab-pane");
44328          bodyEl.insertBefore(body, bodyEl.firstChild);
44329         return body;
44330     },
44331     /** @private */
44332     createStripElements :  function(stripEl, text, closable, tpl)
44333     {
44334         var td = document.createElement("li"); // was td..
44335         td.className = 'nav-item';
44336         
44337         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
44338         
44339         
44340         stripEl.appendChild(td);
44341         /*if(closable){
44342             td.className = "x-tabs-closable";
44343             if(!this.closeTpl){
44344                 this.closeTpl = new Roo.Template(
44345                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44346                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
44347                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
44348                 );
44349             }
44350             var el = this.closeTpl.overwrite(td, {"text": text});
44351             var close = el.getElementsByTagName("div")[0];
44352             var inner = el.getElementsByTagName("em")[0];
44353             return {"el": el, "close": close, "inner": inner};
44354         } else {
44355         */
44356         // not sure what this is..
44357 //            if(!this.tabTpl){
44358                 //this.tabTpl = new Roo.Template(
44359                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44360                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
44361                 //);
44362 //                this.tabTpl = new Roo.Template(
44363 //                   '<a href="#">' +
44364 //                   '<span unselectable="on"' +
44365 //                            (this.disableTooltips ? '' : ' title="{text}"') +
44366 //                            ' >{text}</span></a>'
44367 //                );
44368 //                
44369 //            }
44370
44371
44372             var template = tpl || this.tabTpl || false;
44373             
44374             if(!template){
44375                 template =  new Roo.Template(
44376                         Roo.bootstrap.version == 4 ? 
44377                             (
44378                                 '<a class="nav-link" href="#" unselectable="on"' +
44379                                      (this.disableTooltips ? '' : ' title="{text}"') +
44380                                      ' >{text}</a>'
44381                             ) : (
44382                                 '<a class="nav-link" href="#">' +
44383                                 '<span unselectable="on"' +
44384                                          (this.disableTooltips ? '' : ' title="{text}"') +
44385                                     ' >{text}</span></a>'
44386                             )
44387                 );
44388             }
44389             
44390             switch (typeof(template)) {
44391                 case 'object' :
44392                     break;
44393                 case 'string' :
44394                     template = new Roo.Template(template);
44395                     break;
44396                 default :
44397                     break;
44398             }
44399             
44400             var el = template.overwrite(td, {"text": text});
44401             
44402             var inner = el.getElementsByTagName("span")[0];
44403             
44404             return {"el": el, "inner": inner};
44405             
44406     }
44407         
44408     
44409 });
44410
44411 /**
44412  * @class Roo.TabPanelItem
44413  * @extends Roo.util.Observable
44414  * Represents an individual item (tab plus body) in a TabPanel.
44415  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
44416  * @param {String} id The id of this TabPanelItem
44417  * @param {String} text The text for the tab of this TabPanelItem
44418  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
44419  */
44420 Roo.bootstrap.panel.TabItem = function(config){
44421     /**
44422      * The {@link Roo.TabPanel} this TabPanelItem belongs to
44423      * @type Roo.TabPanel
44424      */
44425     this.tabPanel = config.panel;
44426     /**
44427      * The id for this TabPanelItem
44428      * @type String
44429      */
44430     this.id = config.id;
44431     /** @private */
44432     this.disabled = false;
44433     /** @private */
44434     this.text = config.text;
44435     /** @private */
44436     this.loaded = false;
44437     this.closable = config.closable;
44438
44439     /**
44440      * The body element for this TabPanelItem.
44441      * @type Roo.Element
44442      */
44443     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
44444     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
44445     this.bodyEl.setStyle("display", "block");
44446     this.bodyEl.setStyle("zoom", "1");
44447     //this.hideAction();
44448
44449     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
44450     /** @private */
44451     this.el = Roo.get(els.el);
44452     this.inner = Roo.get(els.inner, true);
44453      this.textEl = Roo.bootstrap.version == 4 ?
44454         this.el : Roo.get(this.el.dom.firstChild, true);
44455
44456     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
44457     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
44458
44459     
44460 //    this.el.on("mousedown", this.onTabMouseDown, this);
44461     this.el.on("click", this.onTabClick, this);
44462     /** @private */
44463     if(config.closable){
44464         var c = Roo.get(els.close, true);
44465         c.dom.title = this.closeText;
44466         c.addClassOnOver("close-over");
44467         c.on("click", this.closeClick, this);
44468      }
44469
44470     this.addEvents({
44471          /**
44472          * @event activate
44473          * Fires when this tab becomes the active tab.
44474          * @param {Roo.TabPanel} tabPanel The parent TabPanel
44475          * @param {Roo.TabPanelItem} this
44476          */
44477         "activate": true,
44478         /**
44479          * @event beforeclose
44480          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
44481          * @param {Roo.TabPanelItem} this
44482          * @param {Object} e Set cancel to true on this object to cancel the close.
44483          */
44484         "beforeclose": true,
44485         /**
44486          * @event close
44487          * Fires when this tab is closed.
44488          * @param {Roo.TabPanelItem} this
44489          */
44490          "close": true,
44491         /**
44492          * @event deactivate
44493          * Fires when this tab is no longer the active tab.
44494          * @param {Roo.TabPanel} tabPanel The parent TabPanel
44495          * @param {Roo.TabPanelItem} this
44496          */
44497          "deactivate" : true
44498     });
44499     this.hidden = false;
44500
44501     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
44502 };
44503
44504 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
44505            {
44506     purgeListeners : function(){
44507        Roo.util.Observable.prototype.purgeListeners.call(this);
44508        this.el.removeAllListeners();
44509     },
44510     /**
44511      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
44512      */
44513     show : function(){
44514         this.status_node.addClass("active");
44515         this.showAction();
44516         if(Roo.isOpera){
44517             this.tabPanel.stripWrap.repaint();
44518         }
44519         this.fireEvent("activate", this.tabPanel, this);
44520     },
44521
44522     /**
44523      * Returns true if this tab is the active tab.
44524      * @return {Boolean}
44525      */
44526     isActive : function(){
44527         return this.tabPanel.getActiveTab() == this;
44528     },
44529
44530     /**
44531      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
44532      */
44533     hide : function(){
44534         this.status_node.removeClass("active");
44535         this.hideAction();
44536         this.fireEvent("deactivate", this.tabPanel, this);
44537     },
44538
44539     hideAction : function(){
44540         this.bodyEl.hide();
44541         this.bodyEl.setStyle("position", "absolute");
44542         this.bodyEl.setLeft("-20000px");
44543         this.bodyEl.setTop("-20000px");
44544     },
44545
44546     showAction : function(){
44547         this.bodyEl.setStyle("position", "relative");
44548         this.bodyEl.setTop("");
44549         this.bodyEl.setLeft("");
44550         this.bodyEl.show();
44551     },
44552
44553     /**
44554      * Set the tooltip for the tab.
44555      * @param {String} tooltip The tab's tooltip
44556      */
44557     setTooltip : function(text){
44558         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
44559             this.textEl.dom.qtip = text;
44560             this.textEl.dom.removeAttribute('title');
44561         }else{
44562             this.textEl.dom.title = text;
44563         }
44564     },
44565
44566     onTabClick : function(e){
44567         e.preventDefault();
44568         this.tabPanel.activate(this.id);
44569     },
44570
44571     onTabMouseDown : function(e){
44572         e.preventDefault();
44573         this.tabPanel.activate(this.id);
44574     },
44575 /*
44576     getWidth : function(){
44577         return this.inner.getWidth();
44578     },
44579
44580     setWidth : function(width){
44581         var iwidth = width - this.linode.getPadding("lr");
44582         this.inner.setWidth(iwidth);
44583         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
44584         this.linode.setWidth(width);
44585     },
44586 */
44587     /**
44588      * Show or hide the tab
44589      * @param {Boolean} hidden True to hide or false to show.
44590      */
44591     setHidden : function(hidden){
44592         this.hidden = hidden;
44593         this.linode.setStyle("display", hidden ? "none" : "");
44594     },
44595
44596     /**
44597      * Returns true if this tab is "hidden"
44598      * @return {Boolean}
44599      */
44600     isHidden : function(){
44601         return this.hidden;
44602     },
44603
44604     /**
44605      * Returns the text for this tab
44606      * @return {String}
44607      */
44608     getText : function(){
44609         return this.text;
44610     },
44611     /*
44612     autoSize : function(){
44613         //this.el.beginMeasure();
44614         this.textEl.setWidth(1);
44615         /*
44616          *  #2804 [new] Tabs in Roojs
44617          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
44618          */
44619         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
44620         //this.el.endMeasure();
44621     //},
44622
44623     /**
44624      * Sets the text for the tab (Note: this also sets the tooltip text)
44625      * @param {String} text The tab's text and tooltip
44626      */
44627     setText : function(text){
44628         this.text = text;
44629         this.textEl.update(text);
44630         this.setTooltip(text);
44631         //if(!this.tabPanel.resizeTabs){
44632         //    this.autoSize();
44633         //}
44634     },
44635     /**
44636      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
44637      */
44638     activate : function(){
44639         this.tabPanel.activate(this.id);
44640     },
44641
44642     /**
44643      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
44644      */
44645     disable : function(){
44646         if(this.tabPanel.active != this){
44647             this.disabled = true;
44648             this.status_node.addClass("disabled");
44649         }
44650     },
44651
44652     /**
44653      * Enables this TabPanelItem if it was previously disabled.
44654      */
44655     enable : function(){
44656         this.disabled = false;
44657         this.status_node.removeClass("disabled");
44658     },
44659
44660     /**
44661      * Sets the content for this TabPanelItem.
44662      * @param {String} content The content
44663      * @param {Boolean} loadScripts true to look for and load scripts
44664      */
44665     setContent : function(content, loadScripts){
44666         this.bodyEl.update(content, loadScripts);
44667     },
44668
44669     /**
44670      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
44671      * @return {Roo.UpdateManager} The UpdateManager
44672      */
44673     getUpdateManager : function(){
44674         return this.bodyEl.getUpdateManager();
44675     },
44676
44677     /**
44678      * Set a URL to be used to load the content for this TabPanelItem.
44679      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
44680      * @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)
44681      * @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)
44682      * @return {Roo.UpdateManager} The UpdateManager
44683      */
44684     setUrl : function(url, params, loadOnce){
44685         if(this.refreshDelegate){
44686             this.un('activate', this.refreshDelegate);
44687         }
44688         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44689         this.on("activate", this.refreshDelegate);
44690         return this.bodyEl.getUpdateManager();
44691     },
44692
44693     /** @private */
44694     _handleRefresh : function(url, params, loadOnce){
44695         if(!loadOnce || !this.loaded){
44696             var updater = this.bodyEl.getUpdateManager();
44697             updater.update(url, params, this._setLoaded.createDelegate(this));
44698         }
44699     },
44700
44701     /**
44702      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
44703      *   Will fail silently if the setUrl method has not been called.
44704      *   This does not activate the panel, just updates its content.
44705      */
44706     refresh : function(){
44707         if(this.refreshDelegate){
44708            this.loaded = false;
44709            this.refreshDelegate();
44710         }
44711     },
44712
44713     /** @private */
44714     _setLoaded : function(){
44715         this.loaded = true;
44716     },
44717
44718     /** @private */
44719     closeClick : function(e){
44720         var o = {};
44721         e.stopEvent();
44722         this.fireEvent("beforeclose", this, o);
44723         if(o.cancel !== true){
44724             this.tabPanel.removeTab(this.id);
44725         }
44726     },
44727     /**
44728      * The text displayed in the tooltip for the close icon.
44729      * @type String
44730      */
44731     closeText : "Close this tab"
44732 });
44733 /**
44734 *    This script refer to:
44735 *    Title: International Telephone Input
44736 *    Author: Jack O'Connor
44737 *    Code version:  v12.1.12
44738 *    Availability: https://github.com/jackocnr/intl-tel-input.git
44739 **/
44740
44741 Roo.bootstrap.form.PhoneInputData = function() {
44742     var d = [
44743       [
44744         "Afghanistan (‫افغانستان‬‎)",
44745         "af",
44746         "93"
44747       ],
44748       [
44749         "Albania (Shqipëri)",
44750         "al",
44751         "355"
44752       ],
44753       [
44754         "Algeria (‫الجزائر‬‎)",
44755         "dz",
44756         "213"
44757       ],
44758       [
44759         "American Samoa",
44760         "as",
44761         "1684"
44762       ],
44763       [
44764         "Andorra",
44765         "ad",
44766         "376"
44767       ],
44768       [
44769         "Angola",
44770         "ao",
44771         "244"
44772       ],
44773       [
44774         "Anguilla",
44775         "ai",
44776         "1264"
44777       ],
44778       [
44779         "Antigua and Barbuda",
44780         "ag",
44781         "1268"
44782       ],
44783       [
44784         "Argentina",
44785         "ar",
44786         "54"
44787       ],
44788       [
44789         "Armenia (Հայաստան)",
44790         "am",
44791         "374"
44792       ],
44793       [
44794         "Aruba",
44795         "aw",
44796         "297"
44797       ],
44798       [
44799         "Australia",
44800         "au",
44801         "61",
44802         0
44803       ],
44804       [
44805         "Austria (Österreich)",
44806         "at",
44807         "43"
44808       ],
44809       [
44810         "Azerbaijan (Azərbaycan)",
44811         "az",
44812         "994"
44813       ],
44814       [
44815         "Bahamas",
44816         "bs",
44817         "1242"
44818       ],
44819       [
44820         "Bahrain (‫البحرين‬‎)",
44821         "bh",
44822         "973"
44823       ],
44824       [
44825         "Bangladesh (বাংলাদেশ)",
44826         "bd",
44827         "880"
44828       ],
44829       [
44830         "Barbados",
44831         "bb",
44832         "1246"
44833       ],
44834       [
44835         "Belarus (Беларусь)",
44836         "by",
44837         "375"
44838       ],
44839       [
44840         "Belgium (België)",
44841         "be",
44842         "32"
44843       ],
44844       [
44845         "Belize",
44846         "bz",
44847         "501"
44848       ],
44849       [
44850         "Benin (Bénin)",
44851         "bj",
44852         "229"
44853       ],
44854       [
44855         "Bermuda",
44856         "bm",
44857         "1441"
44858       ],
44859       [
44860         "Bhutan (འབྲུག)",
44861         "bt",
44862         "975"
44863       ],
44864       [
44865         "Bolivia",
44866         "bo",
44867         "591"
44868       ],
44869       [
44870         "Bosnia and Herzegovina (Босна и Херцеговина)",
44871         "ba",
44872         "387"
44873       ],
44874       [
44875         "Botswana",
44876         "bw",
44877         "267"
44878       ],
44879       [
44880         "Brazil (Brasil)",
44881         "br",
44882         "55"
44883       ],
44884       [
44885         "British Indian Ocean Territory",
44886         "io",
44887         "246"
44888       ],
44889       [
44890         "British Virgin Islands",
44891         "vg",
44892         "1284"
44893       ],
44894       [
44895         "Brunei",
44896         "bn",
44897         "673"
44898       ],
44899       [
44900         "Bulgaria (България)",
44901         "bg",
44902         "359"
44903       ],
44904       [
44905         "Burkina Faso",
44906         "bf",
44907         "226"
44908       ],
44909       [
44910         "Burundi (Uburundi)",
44911         "bi",
44912         "257"
44913       ],
44914       [
44915         "Cambodia (កម្ពុជា)",
44916         "kh",
44917         "855"
44918       ],
44919       [
44920         "Cameroon (Cameroun)",
44921         "cm",
44922         "237"
44923       ],
44924       [
44925         "Canada",
44926         "ca",
44927         "1",
44928         1,
44929         ["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"]
44930       ],
44931       [
44932         "Cape Verde (Kabu Verdi)",
44933         "cv",
44934         "238"
44935       ],
44936       [
44937         "Caribbean Netherlands",
44938         "bq",
44939         "599",
44940         1
44941       ],
44942       [
44943         "Cayman Islands",
44944         "ky",
44945         "1345"
44946       ],
44947       [
44948         "Central African Republic (République centrafricaine)",
44949         "cf",
44950         "236"
44951       ],
44952       [
44953         "Chad (Tchad)",
44954         "td",
44955         "235"
44956       ],
44957       [
44958         "Chile",
44959         "cl",
44960         "56"
44961       ],
44962       [
44963         "China (中国)",
44964         "cn",
44965         "86"
44966       ],
44967       [
44968         "Christmas Island",
44969         "cx",
44970         "61",
44971         2
44972       ],
44973       [
44974         "Cocos (Keeling) Islands",
44975         "cc",
44976         "61",
44977         1
44978       ],
44979       [
44980         "Colombia",
44981         "co",
44982         "57"
44983       ],
44984       [
44985         "Comoros (‫جزر القمر‬‎)",
44986         "km",
44987         "269"
44988       ],
44989       [
44990         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
44991         "cd",
44992         "243"
44993       ],
44994       [
44995         "Congo (Republic) (Congo-Brazzaville)",
44996         "cg",
44997         "242"
44998       ],
44999       [
45000         "Cook Islands",
45001         "ck",
45002         "682"
45003       ],
45004       [
45005         "Costa Rica",
45006         "cr",
45007         "506"
45008       ],
45009       [
45010         "Côte d’Ivoire",
45011         "ci",
45012         "225"
45013       ],
45014       [
45015         "Croatia (Hrvatska)",
45016         "hr",
45017         "385"
45018       ],
45019       [
45020         "Cuba",
45021         "cu",
45022         "53"
45023       ],
45024       [
45025         "Curaçao",
45026         "cw",
45027         "599",
45028         0
45029       ],
45030       [
45031         "Cyprus (Κύπρος)",
45032         "cy",
45033         "357"
45034       ],
45035       [
45036         "Czech Republic (Česká republika)",
45037         "cz",
45038         "420"
45039       ],
45040       [
45041         "Denmark (Danmark)",
45042         "dk",
45043         "45"
45044       ],
45045       [
45046         "Djibouti",
45047         "dj",
45048         "253"
45049       ],
45050       [
45051         "Dominica",
45052         "dm",
45053         "1767"
45054       ],
45055       [
45056         "Dominican Republic (República Dominicana)",
45057         "do",
45058         "1",
45059         2,
45060         ["809", "829", "849"]
45061       ],
45062       [
45063         "Ecuador",
45064         "ec",
45065         "593"
45066       ],
45067       [
45068         "Egypt (‫مصر‬‎)",
45069         "eg",
45070         "20"
45071       ],
45072       [
45073         "El Salvador",
45074         "sv",
45075         "503"
45076       ],
45077       [
45078         "Equatorial Guinea (Guinea Ecuatorial)",
45079         "gq",
45080         "240"
45081       ],
45082       [
45083         "Eritrea",
45084         "er",
45085         "291"
45086       ],
45087       [
45088         "Estonia (Eesti)",
45089         "ee",
45090         "372"
45091       ],
45092       [
45093         "Ethiopia",
45094         "et",
45095         "251"
45096       ],
45097       [
45098         "Falkland Islands (Islas Malvinas)",
45099         "fk",
45100         "500"
45101       ],
45102       [
45103         "Faroe Islands (Føroyar)",
45104         "fo",
45105         "298"
45106       ],
45107       [
45108         "Fiji",
45109         "fj",
45110         "679"
45111       ],
45112       [
45113         "Finland (Suomi)",
45114         "fi",
45115         "358",
45116         0
45117       ],
45118       [
45119         "France",
45120         "fr",
45121         "33"
45122       ],
45123       [
45124         "French Guiana (Guyane française)",
45125         "gf",
45126         "594"
45127       ],
45128       [
45129         "French Polynesia (Polynésie française)",
45130         "pf",
45131         "689"
45132       ],
45133       [
45134         "Gabon",
45135         "ga",
45136         "241"
45137       ],
45138       [
45139         "Gambia",
45140         "gm",
45141         "220"
45142       ],
45143       [
45144         "Georgia (საქართველო)",
45145         "ge",
45146         "995"
45147       ],
45148       [
45149         "Germany (Deutschland)",
45150         "de",
45151         "49"
45152       ],
45153       [
45154         "Ghana (Gaana)",
45155         "gh",
45156         "233"
45157       ],
45158       [
45159         "Gibraltar",
45160         "gi",
45161         "350"
45162       ],
45163       [
45164         "Greece (Ελλάδα)",
45165         "gr",
45166         "30"
45167       ],
45168       [
45169         "Greenland (Kalaallit Nunaat)",
45170         "gl",
45171         "299"
45172       ],
45173       [
45174         "Grenada",
45175         "gd",
45176         "1473"
45177       ],
45178       [
45179         "Guadeloupe",
45180         "gp",
45181         "590",
45182         0
45183       ],
45184       [
45185         "Guam",
45186         "gu",
45187         "1671"
45188       ],
45189       [
45190         "Guatemala",
45191         "gt",
45192         "502"
45193       ],
45194       [
45195         "Guernsey",
45196         "gg",
45197         "44",
45198         1
45199       ],
45200       [
45201         "Guinea (Guinée)",
45202         "gn",
45203         "224"
45204       ],
45205       [
45206         "Guinea-Bissau (Guiné Bissau)",
45207         "gw",
45208         "245"
45209       ],
45210       [
45211         "Guyana",
45212         "gy",
45213         "592"
45214       ],
45215       [
45216         "Haiti",
45217         "ht",
45218         "509"
45219       ],
45220       [
45221         "Honduras",
45222         "hn",
45223         "504"
45224       ],
45225       [
45226         "Hong Kong (香港)",
45227         "hk",
45228         "852"
45229       ],
45230       [
45231         "Hungary (Magyarország)",
45232         "hu",
45233         "36"
45234       ],
45235       [
45236         "Iceland (Ísland)",
45237         "is",
45238         "354"
45239       ],
45240       [
45241         "India (भारत)",
45242         "in",
45243         "91"
45244       ],
45245       [
45246         "Indonesia",
45247         "id",
45248         "62"
45249       ],
45250       [
45251         "Iran (‫ایران‬‎)",
45252         "ir",
45253         "98"
45254       ],
45255       [
45256         "Iraq (‫العراق‬‎)",
45257         "iq",
45258         "964"
45259       ],
45260       [
45261         "Ireland",
45262         "ie",
45263         "353"
45264       ],
45265       [
45266         "Isle of Man",
45267         "im",
45268         "44",
45269         2
45270       ],
45271       [
45272         "Israel (‫ישראל‬‎)",
45273         "il",
45274         "972"
45275       ],
45276       [
45277         "Italy (Italia)",
45278         "it",
45279         "39",
45280         0
45281       ],
45282       [
45283         "Jamaica",
45284         "jm",
45285         "1876"
45286       ],
45287       [
45288         "Japan (日本)",
45289         "jp",
45290         "81"
45291       ],
45292       [
45293         "Jersey",
45294         "je",
45295         "44",
45296         3
45297       ],
45298       [
45299         "Jordan (‫الأردن‬‎)",
45300         "jo",
45301         "962"
45302       ],
45303       [
45304         "Kazakhstan (Казахстан)",
45305         "kz",
45306         "7",
45307         1
45308       ],
45309       [
45310         "Kenya",
45311         "ke",
45312         "254"
45313       ],
45314       [
45315         "Kiribati",
45316         "ki",
45317         "686"
45318       ],
45319       [
45320         "Kosovo",
45321         "xk",
45322         "383"
45323       ],
45324       [
45325         "Kuwait (‫الكويت‬‎)",
45326         "kw",
45327         "965"
45328       ],
45329       [
45330         "Kyrgyzstan (Кыргызстан)",
45331         "kg",
45332         "996"
45333       ],
45334       [
45335         "Laos (ລາວ)",
45336         "la",
45337         "856"
45338       ],
45339       [
45340         "Latvia (Latvija)",
45341         "lv",
45342         "371"
45343       ],
45344       [
45345         "Lebanon (‫لبنان‬‎)",
45346         "lb",
45347         "961"
45348       ],
45349       [
45350         "Lesotho",
45351         "ls",
45352         "266"
45353       ],
45354       [
45355         "Liberia",
45356         "lr",
45357         "231"
45358       ],
45359       [
45360         "Libya (‫ليبيا‬‎)",
45361         "ly",
45362         "218"
45363       ],
45364       [
45365         "Liechtenstein",
45366         "li",
45367         "423"
45368       ],
45369       [
45370         "Lithuania (Lietuva)",
45371         "lt",
45372         "370"
45373       ],
45374       [
45375         "Luxembourg",
45376         "lu",
45377         "352"
45378       ],
45379       [
45380         "Macau (澳門)",
45381         "mo",
45382         "853"
45383       ],
45384       [
45385         "Macedonia (FYROM) (Македонија)",
45386         "mk",
45387         "389"
45388       ],
45389       [
45390         "Madagascar (Madagasikara)",
45391         "mg",
45392         "261"
45393       ],
45394       [
45395         "Malawi",
45396         "mw",
45397         "265"
45398       ],
45399       [
45400         "Malaysia",
45401         "my",
45402         "60"
45403       ],
45404       [
45405         "Maldives",
45406         "mv",
45407         "960"
45408       ],
45409       [
45410         "Mali",
45411         "ml",
45412         "223"
45413       ],
45414       [
45415         "Malta",
45416         "mt",
45417         "356"
45418       ],
45419       [
45420         "Marshall Islands",
45421         "mh",
45422         "692"
45423       ],
45424       [
45425         "Martinique",
45426         "mq",
45427         "596"
45428       ],
45429       [
45430         "Mauritania (‫موريتانيا‬‎)",
45431         "mr",
45432         "222"
45433       ],
45434       [
45435         "Mauritius (Moris)",
45436         "mu",
45437         "230"
45438       ],
45439       [
45440         "Mayotte",
45441         "yt",
45442         "262",
45443         1
45444       ],
45445       [
45446         "Mexico (México)",
45447         "mx",
45448         "52"
45449       ],
45450       [
45451         "Micronesia",
45452         "fm",
45453         "691"
45454       ],
45455       [
45456         "Moldova (Republica Moldova)",
45457         "md",
45458         "373"
45459       ],
45460       [
45461         "Monaco",
45462         "mc",
45463         "377"
45464       ],
45465       [
45466         "Mongolia (Монгол)",
45467         "mn",
45468         "976"
45469       ],
45470       [
45471         "Montenegro (Crna Gora)",
45472         "me",
45473         "382"
45474       ],
45475       [
45476         "Montserrat",
45477         "ms",
45478         "1664"
45479       ],
45480       [
45481         "Morocco (‫المغرب‬‎)",
45482         "ma",
45483         "212",
45484         0
45485       ],
45486       [
45487         "Mozambique (Moçambique)",
45488         "mz",
45489         "258"
45490       ],
45491       [
45492         "Myanmar (Burma) (မြန်မာ)",
45493         "mm",
45494         "95"
45495       ],
45496       [
45497         "Namibia (Namibië)",
45498         "na",
45499         "264"
45500       ],
45501       [
45502         "Nauru",
45503         "nr",
45504         "674"
45505       ],
45506       [
45507         "Nepal (नेपाल)",
45508         "np",
45509         "977"
45510       ],
45511       [
45512         "Netherlands (Nederland)",
45513         "nl",
45514         "31"
45515       ],
45516       [
45517         "New Caledonia (Nouvelle-Calédonie)",
45518         "nc",
45519         "687"
45520       ],
45521       [
45522         "New Zealand",
45523         "nz",
45524         "64"
45525       ],
45526       [
45527         "Nicaragua",
45528         "ni",
45529         "505"
45530       ],
45531       [
45532         "Niger (Nijar)",
45533         "ne",
45534         "227"
45535       ],
45536       [
45537         "Nigeria",
45538         "ng",
45539         "234"
45540       ],
45541       [
45542         "Niue",
45543         "nu",
45544         "683"
45545       ],
45546       [
45547         "Norfolk Island",
45548         "nf",
45549         "672"
45550       ],
45551       [
45552         "North Korea (조선 민주주의 인민 공화국)",
45553         "kp",
45554         "850"
45555       ],
45556       [
45557         "Northern Mariana Islands",
45558         "mp",
45559         "1670"
45560       ],
45561       [
45562         "Norway (Norge)",
45563         "no",
45564         "47",
45565         0
45566       ],
45567       [
45568         "Oman (‫عُمان‬‎)",
45569         "om",
45570         "968"
45571       ],
45572       [
45573         "Pakistan (‫پاکستان‬‎)",
45574         "pk",
45575         "92"
45576       ],
45577       [
45578         "Palau",
45579         "pw",
45580         "680"
45581       ],
45582       [
45583         "Palestine (‫فلسطين‬‎)",
45584         "ps",
45585         "970"
45586       ],
45587       [
45588         "Panama (Panamá)",
45589         "pa",
45590         "507"
45591       ],
45592       [
45593         "Papua New Guinea",
45594         "pg",
45595         "675"
45596       ],
45597       [
45598         "Paraguay",
45599         "py",
45600         "595"
45601       ],
45602       [
45603         "Peru (Perú)",
45604         "pe",
45605         "51"
45606       ],
45607       [
45608         "Philippines",
45609         "ph",
45610         "63"
45611       ],
45612       [
45613         "Poland (Polska)",
45614         "pl",
45615         "48"
45616       ],
45617       [
45618         "Portugal",
45619         "pt",
45620         "351"
45621       ],
45622       [
45623         "Puerto Rico",
45624         "pr",
45625         "1",
45626         3,
45627         ["787", "939"]
45628       ],
45629       [
45630         "Qatar (‫قطر‬‎)",
45631         "qa",
45632         "974"
45633       ],
45634       [
45635         "Réunion (La Réunion)",
45636         "re",
45637         "262",
45638         0
45639       ],
45640       [
45641         "Romania (România)",
45642         "ro",
45643         "40"
45644       ],
45645       [
45646         "Russia (Россия)",
45647         "ru",
45648         "7",
45649         0
45650       ],
45651       [
45652         "Rwanda",
45653         "rw",
45654         "250"
45655       ],
45656       [
45657         "Saint Barthélemy",
45658         "bl",
45659         "590",
45660         1
45661       ],
45662       [
45663         "Saint Helena",
45664         "sh",
45665         "290"
45666       ],
45667       [
45668         "Saint Kitts and Nevis",
45669         "kn",
45670         "1869"
45671       ],
45672       [
45673         "Saint Lucia",
45674         "lc",
45675         "1758"
45676       ],
45677       [
45678         "Saint Martin (Saint-Martin (partie française))",
45679         "mf",
45680         "590",
45681         2
45682       ],
45683       [
45684         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
45685         "pm",
45686         "508"
45687       ],
45688       [
45689         "Saint Vincent and the Grenadines",
45690         "vc",
45691         "1784"
45692       ],
45693       [
45694         "Samoa",
45695         "ws",
45696         "685"
45697       ],
45698       [
45699         "San Marino",
45700         "sm",
45701         "378"
45702       ],
45703       [
45704         "São Tomé and Príncipe (São Tomé e Príncipe)",
45705         "st",
45706         "239"
45707       ],
45708       [
45709         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
45710         "sa",
45711         "966"
45712       ],
45713       [
45714         "Senegal (Sénégal)",
45715         "sn",
45716         "221"
45717       ],
45718       [
45719         "Serbia (Србија)",
45720         "rs",
45721         "381"
45722       ],
45723       [
45724         "Seychelles",
45725         "sc",
45726         "248"
45727       ],
45728       [
45729         "Sierra Leone",
45730         "sl",
45731         "232"
45732       ],
45733       [
45734         "Singapore",
45735         "sg",
45736         "65"
45737       ],
45738       [
45739         "Sint Maarten",
45740         "sx",
45741         "1721"
45742       ],
45743       [
45744         "Slovakia (Slovensko)",
45745         "sk",
45746         "421"
45747       ],
45748       [
45749         "Slovenia (Slovenija)",
45750         "si",
45751         "386"
45752       ],
45753       [
45754         "Solomon Islands",
45755         "sb",
45756         "677"
45757       ],
45758       [
45759         "Somalia (Soomaaliya)",
45760         "so",
45761         "252"
45762       ],
45763       [
45764         "South Africa",
45765         "za",
45766         "27"
45767       ],
45768       [
45769         "South Korea (대한민국)",
45770         "kr",
45771         "82"
45772       ],
45773       [
45774         "South Sudan (‫جنوب السودان‬‎)",
45775         "ss",
45776         "211"
45777       ],
45778       [
45779         "Spain (España)",
45780         "es",
45781         "34"
45782       ],
45783       [
45784         "Sri Lanka (ශ්‍රී ලංකාව)",
45785         "lk",
45786         "94"
45787       ],
45788       [
45789         "Sudan (‫السودان‬‎)",
45790         "sd",
45791         "249"
45792       ],
45793       [
45794         "Suriname",
45795         "sr",
45796         "597"
45797       ],
45798       [
45799         "Svalbard and Jan Mayen",
45800         "sj",
45801         "47",
45802         1
45803       ],
45804       [
45805         "Swaziland",
45806         "sz",
45807         "268"
45808       ],
45809       [
45810         "Sweden (Sverige)",
45811         "se",
45812         "46"
45813       ],
45814       [
45815         "Switzerland (Schweiz)",
45816         "ch",
45817         "41"
45818       ],
45819       [
45820         "Syria (‫سوريا‬‎)",
45821         "sy",
45822         "963"
45823       ],
45824       [
45825         "Taiwan (台灣)",
45826         "tw",
45827         "886"
45828       ],
45829       [
45830         "Tajikistan",
45831         "tj",
45832         "992"
45833       ],
45834       [
45835         "Tanzania",
45836         "tz",
45837         "255"
45838       ],
45839       [
45840         "Thailand (ไทย)",
45841         "th",
45842         "66"
45843       ],
45844       [
45845         "Timor-Leste",
45846         "tl",
45847         "670"
45848       ],
45849       [
45850         "Togo",
45851         "tg",
45852         "228"
45853       ],
45854       [
45855         "Tokelau",
45856         "tk",
45857         "690"
45858       ],
45859       [
45860         "Tonga",
45861         "to",
45862         "676"
45863       ],
45864       [
45865         "Trinidad and Tobago",
45866         "tt",
45867         "1868"
45868       ],
45869       [
45870         "Tunisia (‫تونس‬‎)",
45871         "tn",
45872         "216"
45873       ],
45874       [
45875         "Turkey (Türkiye)",
45876         "tr",
45877         "90"
45878       ],
45879       [
45880         "Turkmenistan",
45881         "tm",
45882         "993"
45883       ],
45884       [
45885         "Turks and Caicos Islands",
45886         "tc",
45887         "1649"
45888       ],
45889       [
45890         "Tuvalu",
45891         "tv",
45892         "688"
45893       ],
45894       [
45895         "U.S. Virgin Islands",
45896         "vi",
45897         "1340"
45898       ],
45899       [
45900         "Uganda",
45901         "ug",
45902         "256"
45903       ],
45904       [
45905         "Ukraine (Україна)",
45906         "ua",
45907         "380"
45908       ],
45909       [
45910         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
45911         "ae",
45912         "971"
45913       ],
45914       [
45915         "United Kingdom",
45916         "gb",
45917         "44",
45918         0
45919       ],
45920       [
45921         "United States",
45922         "us",
45923         "1",
45924         0
45925       ],
45926       [
45927         "Uruguay",
45928         "uy",
45929         "598"
45930       ],
45931       [
45932         "Uzbekistan (Oʻzbekiston)",
45933         "uz",
45934         "998"
45935       ],
45936       [
45937         "Vanuatu",
45938         "vu",
45939         "678"
45940       ],
45941       [
45942         "Vatican City (Città del Vaticano)",
45943         "va",
45944         "39",
45945         1
45946       ],
45947       [
45948         "Venezuela",
45949         "ve",
45950         "58"
45951       ],
45952       [
45953         "Vietnam (Việt Nam)",
45954         "vn",
45955         "84"
45956       ],
45957       [
45958         "Wallis and Futuna (Wallis-et-Futuna)",
45959         "wf",
45960         "681"
45961       ],
45962       [
45963         "Western Sahara (‫الصحراء الغربية‬‎)",
45964         "eh",
45965         "212",
45966         1
45967       ],
45968       [
45969         "Yemen (‫اليمن‬‎)",
45970         "ye",
45971         "967"
45972       ],
45973       [
45974         "Zambia",
45975         "zm",
45976         "260"
45977       ],
45978       [
45979         "Zimbabwe",
45980         "zw",
45981         "263"
45982       ],
45983       [
45984         "Åland Islands",
45985         "ax",
45986         "358",
45987         1
45988       ]
45989   ];
45990   
45991   return d;
45992 }/**
45993 *    This script refer to:
45994 *    Title: International Telephone Input
45995 *    Author: Jack O'Connor
45996 *    Code version:  v12.1.12
45997 *    Availability: https://github.com/jackocnr/intl-tel-input.git
45998 **/
45999
46000 /**
46001  * @class Roo.bootstrap.form.PhoneInput
46002  * @extends Roo.bootstrap.form.TriggerField
46003  * An input with International dial-code selection
46004  
46005  * @cfg {String} defaultDialCode default '+852'
46006  * @cfg {Array} preferedCountries default []
46007   
46008  * @constructor
46009  * Create a new PhoneInput.
46010  * @param {Object} config Configuration options
46011  */
46012
46013 Roo.bootstrap.form.PhoneInput = function(config) {
46014     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
46015 };
46016
46017 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
46018         /**
46019         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
46020         */
46021         listWidth: undefined,
46022         
46023         selectedClass: 'active',
46024         
46025         invalidClass : "has-warning",
46026         
46027         validClass: 'has-success',
46028         
46029         allowed: '0123456789',
46030         
46031         max_length: 15,
46032         
46033         /**
46034          * @cfg {String} defaultDialCode The default dial code when initializing the input
46035          */
46036         defaultDialCode: '+852',
46037         
46038         /**
46039          * @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
46040          */
46041         preferedCountries: false,
46042         
46043         getAutoCreate : function()
46044         {
46045             var data = Roo.bootstrap.form.PhoneInputData();
46046             var align = this.labelAlign || this.parentLabelAlign();
46047             var id = Roo.id();
46048             
46049             this.allCountries = [];
46050             this.dialCodeMapping = [];
46051             
46052             for (var i = 0; i < data.length; i++) {
46053               var c = data[i];
46054               this.allCountries[i] = {
46055                 name: c[0],
46056                 iso2: c[1],
46057                 dialCode: c[2],
46058                 priority: c[3] || 0,
46059                 areaCodes: c[4] || null
46060               };
46061               this.dialCodeMapping[c[2]] = {
46062                   name: c[0],
46063                   iso2: c[1],
46064                   priority: c[3] || 0,
46065                   areaCodes: c[4] || null
46066               };
46067             }
46068             
46069             var cfg = {
46070                 cls: 'form-group',
46071                 cn: []
46072             };
46073             
46074             var input =  {
46075                 tag: 'input',
46076                 id : id,
46077                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
46078                 maxlength: this.max_length,
46079                 cls : 'form-control tel-input',
46080                 autocomplete: 'new-password'
46081             };
46082             
46083             var hiddenInput = {
46084                 tag: 'input',
46085                 type: 'hidden',
46086                 cls: 'hidden-tel-input'
46087             };
46088             
46089             if (this.name) {
46090                 hiddenInput.name = this.name;
46091             }
46092             
46093             if (this.disabled) {
46094                 input.disabled = true;
46095             }
46096             
46097             var flag_container = {
46098                 tag: 'div',
46099                 cls: 'flag-box',
46100                 cn: [
46101                     {
46102                         tag: 'div',
46103                         cls: 'flag'
46104                     },
46105                     {
46106                         tag: 'div',
46107                         cls: 'caret'
46108                     }
46109                 ]
46110             };
46111             
46112             var box = {
46113                 tag: 'div',
46114                 cls: this.hasFeedback ? 'has-feedback' : '',
46115                 cn: [
46116                     hiddenInput,
46117                     input,
46118                     {
46119                         tag: 'input',
46120                         cls: 'dial-code-holder',
46121                         disabled: true
46122                     }
46123                 ]
46124             };
46125             
46126             var container = {
46127                 cls: 'roo-select2-container input-group',
46128                 cn: [
46129                     flag_container,
46130                     box
46131                 ]
46132             };
46133             
46134             if (this.fieldLabel.length) {
46135                 var indicator = {
46136                     tag: 'i',
46137                     tooltip: 'This field is required'
46138                 };
46139                 
46140                 var label = {
46141                     tag: 'label',
46142                     'for':  id,
46143                     cls: 'control-label',
46144                     cn: []
46145                 };
46146                 
46147                 var label_text = {
46148                     tag: 'span',
46149                     html: this.fieldLabel
46150                 };
46151                 
46152                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46153                 label.cn = [
46154                     indicator,
46155                     label_text
46156                 ];
46157                 
46158                 if(this.indicatorpos == 'right') {
46159                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46160                     label.cn = [
46161                         label_text,
46162                         indicator
46163                     ];
46164                 }
46165                 
46166                 if(align == 'left') {
46167                     container = {
46168                         tag: 'div',
46169                         cn: [
46170                             container
46171                         ]
46172                     };
46173                     
46174                     if(this.labelWidth > 12){
46175                         label.style = "width: " + this.labelWidth + 'px';
46176                     }
46177                     if(this.labelWidth < 13 && this.labelmd == 0){
46178                         this.labelmd = this.labelWidth;
46179                     }
46180                     if(this.labellg > 0){
46181                         label.cls += ' col-lg-' + this.labellg;
46182                         input.cls += ' col-lg-' + (12 - this.labellg);
46183                     }
46184                     if(this.labelmd > 0){
46185                         label.cls += ' col-md-' + this.labelmd;
46186                         container.cls += ' col-md-' + (12 - this.labelmd);
46187                     }
46188                     if(this.labelsm > 0){
46189                         label.cls += ' col-sm-' + this.labelsm;
46190                         container.cls += ' col-sm-' + (12 - this.labelsm);
46191                     }
46192                     if(this.labelxs > 0){
46193                         label.cls += ' col-xs-' + this.labelxs;
46194                         container.cls += ' col-xs-' + (12 - this.labelxs);
46195                     }
46196                 }
46197             }
46198             
46199             cfg.cn = [
46200                 label,
46201                 container
46202             ];
46203             
46204             var settings = this;
46205             
46206             ['xs','sm','md','lg'].map(function(size){
46207                 if (settings[size]) {
46208                     cfg.cls += ' col-' + size + '-' + settings[size];
46209                 }
46210             });
46211             
46212             this.store = new Roo.data.Store({
46213                 proxy : new Roo.data.MemoryProxy({}),
46214                 reader : new Roo.data.JsonReader({
46215                     fields : [
46216                         {
46217                             'name' : 'name',
46218                             'type' : 'string'
46219                         },
46220                         {
46221                             'name' : 'iso2',
46222                             'type' : 'string'
46223                         },
46224                         {
46225                             'name' : 'dialCode',
46226                             'type' : 'string'
46227                         },
46228                         {
46229                             'name' : 'priority',
46230                             'type' : 'string'
46231                         },
46232                         {
46233                             'name' : 'areaCodes',
46234                             'type' : 'string'
46235                         }
46236                     ]
46237                 })
46238             });
46239             
46240             if(!this.preferedCountries) {
46241                 this.preferedCountries = [
46242                     'hk',
46243                     'gb',
46244                     'us'
46245                 ];
46246             }
46247             
46248             var p = this.preferedCountries.reverse();
46249             
46250             if(p) {
46251                 for (var i = 0; i < p.length; i++) {
46252                     for (var j = 0; j < this.allCountries.length; j++) {
46253                         if(this.allCountries[j].iso2 == p[i]) {
46254                             var t = this.allCountries[j];
46255                             this.allCountries.splice(j,1);
46256                             this.allCountries.unshift(t);
46257                         }
46258                     } 
46259                 }
46260             }
46261             
46262             this.store.proxy.data = {
46263                 success: true,
46264                 data: this.allCountries
46265             };
46266             
46267             return cfg;
46268         },
46269         
46270         initEvents : function()
46271         {
46272             this.createList();
46273             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
46274             
46275             this.indicator = this.indicatorEl();
46276             this.flag = this.flagEl();
46277             this.dialCodeHolder = this.dialCodeHolderEl();
46278             
46279             this.trigger = this.el.select('div.flag-box',true).first();
46280             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
46281             
46282             var _this = this;
46283             
46284             (function(){
46285                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
46286                 _this.list.setWidth(lw);
46287             }).defer(100);
46288             
46289             this.list.on('mouseover', this.onViewOver, this);
46290             this.list.on('mousemove', this.onViewMove, this);
46291             this.inputEl().on("keyup", this.onKeyUp, this);
46292             this.inputEl().on("keypress", this.onKeyPress, this);
46293             
46294             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
46295
46296             this.view = new Roo.View(this.list, this.tpl, {
46297                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
46298             });
46299             
46300             this.view.on('click', this.onViewClick, this);
46301             this.setValue(this.defaultDialCode);
46302         },
46303         
46304         onTriggerClick : function(e)
46305         {
46306             Roo.log('trigger click');
46307             if(this.disabled){
46308                 return;
46309             }
46310             
46311             if(this.isExpanded()){
46312                 this.collapse();
46313                 this.hasFocus = false;
46314             }else {
46315                 this.store.load({});
46316                 this.hasFocus = true;
46317                 this.expand();
46318             }
46319         },
46320         
46321         isExpanded : function()
46322         {
46323             return this.list.isVisible();
46324         },
46325         
46326         collapse : function()
46327         {
46328             if(!this.isExpanded()){
46329                 return;
46330             }
46331             this.list.hide();
46332             Roo.get(document).un('mousedown', this.collapseIf, this);
46333             Roo.get(document).un('mousewheel', this.collapseIf, this);
46334             this.fireEvent('collapse', this);
46335             this.validate();
46336         },
46337         
46338         expand : function()
46339         {
46340             Roo.log('expand');
46341
46342             if(this.isExpanded() || !this.hasFocus){
46343                 return;
46344             }
46345             
46346             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
46347             this.list.setWidth(lw);
46348             
46349             this.list.show();
46350             this.restrictHeight();
46351             
46352             Roo.get(document).on('mousedown', this.collapseIf, this);
46353             Roo.get(document).on('mousewheel', this.collapseIf, this);
46354             
46355             this.fireEvent('expand', this);
46356         },
46357         
46358         restrictHeight : function()
46359         {
46360             this.list.alignTo(this.inputEl(), this.listAlign);
46361             this.list.alignTo(this.inputEl(), this.listAlign);
46362         },
46363         
46364         onViewOver : function(e, t)
46365         {
46366             if(this.inKeyMode){
46367                 return;
46368             }
46369             var item = this.view.findItemFromChild(t);
46370             
46371             if(item){
46372                 var index = this.view.indexOf(item);
46373                 this.select(index, false);
46374             }
46375         },
46376
46377         // private
46378         onViewClick : function(view, doFocus, el, e)
46379         {
46380             var index = this.view.getSelectedIndexes()[0];
46381             
46382             var r = this.store.getAt(index);
46383             
46384             if(r){
46385                 this.onSelect(r, index);
46386             }
46387             if(doFocus !== false && !this.blockFocus){
46388                 this.inputEl().focus();
46389             }
46390         },
46391         
46392         onViewMove : function(e, t)
46393         {
46394             this.inKeyMode = false;
46395         },
46396         
46397         select : function(index, scrollIntoView)
46398         {
46399             this.selectedIndex = index;
46400             this.view.select(index);
46401             if(scrollIntoView !== false){
46402                 var el = this.view.getNode(index);
46403                 if(el){
46404                     this.list.scrollChildIntoView(el, false);
46405                 }
46406             }
46407         },
46408         
46409         createList : function()
46410         {
46411             this.list = Roo.get(document.body).createChild({
46412                 tag: 'ul',
46413                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
46414                 style: 'display:none'
46415             });
46416             
46417             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
46418         },
46419         
46420         collapseIf : function(e)
46421         {
46422             var in_combo  = e.within(this.el);
46423             var in_list =  e.within(this.list);
46424             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
46425             
46426             if (in_combo || in_list || is_list) {
46427                 return;
46428             }
46429             this.collapse();
46430         },
46431         
46432         onSelect : function(record, index)
46433         {
46434             if(this.fireEvent('beforeselect', this, record, index) !== false){
46435                 
46436                 this.setFlagClass(record.data.iso2);
46437                 this.setDialCode(record.data.dialCode);
46438                 this.hasFocus = false;
46439                 this.collapse();
46440                 this.fireEvent('select', this, record, index);
46441             }
46442         },
46443         
46444         flagEl : function()
46445         {
46446             var flag = this.el.select('div.flag',true).first();
46447             if(!flag){
46448                 return false;
46449             }
46450             return flag;
46451         },
46452         
46453         dialCodeHolderEl : function()
46454         {
46455             var d = this.el.select('input.dial-code-holder',true).first();
46456             if(!d){
46457                 return false;
46458             }
46459             return d;
46460         },
46461         
46462         setDialCode : function(v)
46463         {
46464             this.dialCodeHolder.dom.value = '+'+v;
46465         },
46466         
46467         setFlagClass : function(n)
46468         {
46469             this.flag.dom.className = 'flag '+n;
46470         },
46471         
46472         getValue : function()
46473         {
46474             var v = this.inputEl().getValue();
46475             if(this.dialCodeHolder) {
46476                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
46477             }
46478             return v;
46479         },
46480         
46481         setValue : function(v)
46482         {
46483             var d = this.getDialCode(v);
46484             
46485             //invalid dial code
46486             if(v.length == 0 || !d || d.length == 0) {
46487                 if(this.rendered){
46488                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
46489                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
46490                 }
46491                 return;
46492             }
46493             
46494             //valid dial code
46495             this.setFlagClass(this.dialCodeMapping[d].iso2);
46496             this.setDialCode(d);
46497             this.inputEl().dom.value = v.replace('+'+d,'');
46498             this.hiddenEl().dom.value = this.getValue();
46499             
46500             this.validate();
46501         },
46502         
46503         getDialCode : function(v)
46504         {
46505             v = v ||  '';
46506             
46507             if (v.length == 0) {
46508                 return this.dialCodeHolder.dom.value;
46509             }
46510             
46511             var dialCode = "";
46512             if (v.charAt(0) != "+") {
46513                 return false;
46514             }
46515             var numericChars = "";
46516             for (var i = 1; i < v.length; i++) {
46517               var c = v.charAt(i);
46518               if (!isNaN(c)) {
46519                 numericChars += c;
46520                 if (this.dialCodeMapping[numericChars]) {
46521                   dialCode = v.substr(1, i);
46522                 }
46523                 if (numericChars.length == 4) {
46524                   break;
46525                 }
46526               }
46527             }
46528             return dialCode;
46529         },
46530         
46531         reset : function()
46532         {
46533             this.setValue(this.defaultDialCode);
46534             this.validate();
46535         },
46536         
46537         hiddenEl : function()
46538         {
46539             return this.el.select('input.hidden-tel-input',true).first();
46540         },
46541         
46542         // after setting val
46543         onKeyUp : function(e){
46544             this.setValue(this.getValue());
46545         },
46546         
46547         onKeyPress : function(e){
46548             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
46549                 e.stopEvent();
46550             }
46551         }
46552         
46553 });
46554 /**
46555  * @class Roo.bootstrap.form.MoneyField
46556  * @extends Roo.bootstrap.form.ComboBox
46557  * Bootstrap MoneyField class
46558  * 
46559  * @constructor
46560  * Create a new MoneyField.
46561  * @param {Object} config Configuration options
46562  */
46563
46564 Roo.bootstrap.form.MoneyField = function(config) {
46565     
46566     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
46567     
46568 };
46569
46570 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
46571     
46572     /**
46573      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
46574      */
46575     allowDecimals : true,
46576     /**
46577      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
46578      */
46579     decimalSeparator : ".",
46580     /**
46581      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
46582      */
46583     decimalPrecision : 0,
46584     /**
46585      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
46586      */
46587     allowNegative : true,
46588     /**
46589      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
46590      */
46591     allowZero: true,
46592     /**
46593      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
46594      */
46595     minValue : Number.NEGATIVE_INFINITY,
46596     /**
46597      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
46598      */
46599     maxValue : Number.MAX_VALUE,
46600     /**
46601      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
46602      */
46603     minText : "The minimum value for this field is {0}",
46604     /**
46605      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
46606      */
46607     maxText : "The maximum value for this field is {0}",
46608     /**
46609      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
46610      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
46611      */
46612     nanText : "{0} is not a valid number",
46613     /**
46614      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
46615      */
46616     castInt : true,
46617     /**
46618      * @cfg {String} defaults currency of the MoneyField
46619      * value should be in lkey
46620      */
46621     defaultCurrency : false,
46622     /**
46623      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
46624      */
46625     thousandsDelimiter : false,
46626     /**
46627      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
46628      */
46629     max_length: false,
46630     
46631     inputlg : 9,
46632     inputmd : 9,
46633     inputsm : 9,
46634     inputxs : 6,
46635      /**
46636      * @cfg {Roo.data.Store} store  Store to lookup currency??
46637      */
46638     store : false,
46639     
46640     getAutoCreate : function()
46641     {
46642         var align = this.labelAlign || this.parentLabelAlign();
46643         
46644         var id = Roo.id();
46645
46646         var cfg = {
46647             cls: 'form-group',
46648             cn: []
46649         };
46650
46651         var input =  {
46652             tag: 'input',
46653             id : id,
46654             cls : 'form-control roo-money-amount-input',
46655             autocomplete: 'new-password'
46656         };
46657         
46658         var hiddenInput = {
46659             tag: 'input',
46660             type: 'hidden',
46661             id: Roo.id(),
46662             cls: 'hidden-number-input'
46663         };
46664         
46665         if(this.max_length) {
46666             input.maxlength = this.max_length; 
46667         }
46668         
46669         if (this.name) {
46670             hiddenInput.name = this.name;
46671         }
46672
46673         if (this.disabled) {
46674             input.disabled = true;
46675         }
46676
46677         var clg = 12 - this.inputlg;
46678         var cmd = 12 - this.inputmd;
46679         var csm = 12 - this.inputsm;
46680         var cxs = 12 - this.inputxs;
46681         
46682         var container = {
46683             tag : 'div',
46684             cls : 'row roo-money-field',
46685             cn : [
46686                 {
46687                     tag : 'div',
46688                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
46689                     cn : [
46690                         {
46691                             tag : 'div',
46692                             cls: 'roo-select2-container input-group',
46693                             cn: [
46694                                 {
46695                                     tag : 'input',
46696                                     cls : 'form-control roo-money-currency-input',
46697                                     autocomplete: 'new-password',
46698                                     readOnly : 1,
46699                                     name : this.currencyName
46700                                 },
46701                                 {
46702                                     tag :'span',
46703                                     cls : 'input-group-addon',
46704                                     cn : [
46705                                         {
46706                                             tag: 'span',
46707                                             cls: 'caret'
46708                                         }
46709                                     ]
46710                                 }
46711                             ]
46712                         }
46713                     ]
46714                 },
46715                 {
46716                     tag : 'div',
46717                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
46718                     cn : [
46719                         {
46720                             tag: 'div',
46721                             cls: this.hasFeedback ? 'has-feedback' : '',
46722                             cn: [
46723                                 input
46724                             ]
46725                         }
46726                     ]
46727                 }
46728             ]
46729             
46730         };
46731         
46732         if (this.fieldLabel.length) {
46733             var indicator = {
46734                 tag: 'i',
46735                 tooltip: 'This field is required'
46736             };
46737
46738             var label = {
46739                 tag: 'label',
46740                 'for':  id,
46741                 cls: 'control-label',
46742                 cn: []
46743             };
46744
46745             var label_text = {
46746                 tag: 'span',
46747                 html: this.fieldLabel
46748             };
46749
46750             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46751             label.cn = [
46752                 indicator,
46753                 label_text
46754             ];
46755
46756             if(this.indicatorpos == 'right') {
46757                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46758                 label.cn = [
46759                     label_text,
46760                     indicator
46761                 ];
46762             }
46763
46764             if(align == 'left') {
46765                 container = {
46766                     tag: 'div',
46767                     cn: [
46768                         container
46769                     ]
46770                 };
46771
46772                 if(this.labelWidth > 12){
46773                     label.style = "width: " + this.labelWidth + 'px';
46774                 }
46775                 if(this.labelWidth < 13 && this.labelmd == 0){
46776                     this.labelmd = this.labelWidth;
46777                 }
46778                 if(this.labellg > 0){
46779                     label.cls += ' col-lg-' + this.labellg;
46780                     input.cls += ' col-lg-' + (12 - this.labellg);
46781                 }
46782                 if(this.labelmd > 0){
46783                     label.cls += ' col-md-' + this.labelmd;
46784                     container.cls += ' col-md-' + (12 - this.labelmd);
46785                 }
46786                 if(this.labelsm > 0){
46787                     label.cls += ' col-sm-' + this.labelsm;
46788                     container.cls += ' col-sm-' + (12 - this.labelsm);
46789                 }
46790                 if(this.labelxs > 0){
46791                     label.cls += ' col-xs-' + this.labelxs;
46792                     container.cls += ' col-xs-' + (12 - this.labelxs);
46793                 }
46794             }
46795         }
46796
46797         cfg.cn = [
46798             label,
46799             container,
46800             hiddenInput
46801         ];
46802         
46803         var settings = this;
46804
46805         ['xs','sm','md','lg'].map(function(size){
46806             if (settings[size]) {
46807                 cfg.cls += ' col-' + size + '-' + settings[size];
46808             }
46809         });
46810         
46811         return cfg;
46812     },
46813     
46814     initEvents : function()
46815     {
46816         this.indicator = this.indicatorEl();
46817         
46818         this.initCurrencyEvent();
46819         
46820         this.initNumberEvent();
46821     },
46822     
46823     initCurrencyEvent : function()
46824     {
46825         if (!this.store) {
46826             throw "can not find store for combo";
46827         }
46828         
46829         this.store = Roo.factory(this.store, Roo.data);
46830         this.store.parent = this;
46831         
46832         this.createList();
46833         
46834         this.triggerEl = this.el.select('.input-group-addon', true).first();
46835         
46836         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
46837         
46838         var _this = this;
46839         
46840         (function(){
46841             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
46842             _this.list.setWidth(lw);
46843         }).defer(100);
46844         
46845         this.list.on('mouseover', this.onViewOver, this);
46846         this.list.on('mousemove', this.onViewMove, this);
46847         this.list.on('scroll', this.onViewScroll, this);
46848         
46849         if(!this.tpl){
46850             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
46851         }
46852         
46853         this.view = new Roo.View(this.list, this.tpl, {
46854             singleSelect:true, store: this.store, selectedClass: this.selectedClass
46855         });
46856         
46857         this.view.on('click', this.onViewClick, this);
46858         
46859         this.store.on('beforeload', this.onBeforeLoad, this);
46860         this.store.on('load', this.onLoad, this);
46861         this.store.on('loadexception', this.onLoadException, this);
46862         
46863         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
46864             "up" : function(e){
46865                 this.inKeyMode = true;
46866                 this.selectPrev();
46867             },
46868
46869             "down" : function(e){
46870                 if(!this.isExpanded()){
46871                     this.onTriggerClick();
46872                 }else{
46873                     this.inKeyMode = true;
46874                     this.selectNext();
46875                 }
46876             },
46877
46878             "enter" : function(e){
46879                 this.collapse();
46880                 
46881                 if(this.fireEvent("specialkey", this, e)){
46882                     this.onViewClick(false);
46883                 }
46884                 
46885                 return true;
46886             },
46887
46888             "esc" : function(e){
46889                 this.collapse();
46890             },
46891
46892             "tab" : function(e){
46893                 this.collapse();
46894                 
46895                 if(this.fireEvent("specialkey", this, e)){
46896                     this.onViewClick(false);
46897                 }
46898                 
46899                 return true;
46900             },
46901
46902             scope : this,
46903
46904             doRelay : function(foo, bar, hname){
46905                 if(hname == 'down' || this.scope.isExpanded()){
46906                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
46907                 }
46908                 return true;
46909             },
46910
46911             forceKeyDown: true
46912         });
46913         
46914         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
46915         
46916     },
46917     
46918     initNumberEvent : function(e)
46919     {
46920         this.inputEl().on("keydown" , this.fireKey,  this);
46921         this.inputEl().on("focus", this.onFocus,  this);
46922         this.inputEl().on("blur", this.onBlur,  this);
46923         
46924         this.inputEl().relayEvent('keyup', this);
46925         
46926         if(this.indicator){
46927             this.indicator.addClass('invisible');
46928         }
46929  
46930         this.originalValue = this.getValue();
46931         
46932         if(this.validationEvent == 'keyup'){
46933             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
46934             this.inputEl().on('keyup', this.filterValidation, this);
46935         }
46936         else if(this.validationEvent !== false){
46937             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
46938         }
46939         
46940         if(this.selectOnFocus){
46941             this.on("focus", this.preFocus, this);
46942             
46943         }
46944         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
46945             this.inputEl().on("keypress", this.filterKeys, this);
46946         } else {
46947             this.inputEl().relayEvent('keypress', this);
46948         }
46949         
46950         var allowed = "0123456789";
46951         
46952         if(this.allowDecimals){
46953             allowed += this.decimalSeparator;
46954         }
46955         
46956         if(this.allowNegative){
46957             allowed += "-";
46958         }
46959         
46960         if(this.thousandsDelimiter) {
46961             allowed += ",";
46962         }
46963         
46964         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
46965         
46966         var keyPress = function(e){
46967             
46968             var k = e.getKey();
46969             
46970             var c = e.getCharCode();
46971             
46972             if(
46973                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
46974                     allowed.indexOf(String.fromCharCode(c)) === -1
46975             ){
46976                 e.stopEvent();
46977                 return;
46978             }
46979             
46980             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
46981                 return;
46982             }
46983             
46984             if(allowed.indexOf(String.fromCharCode(c)) === -1){
46985                 e.stopEvent();
46986             }
46987         };
46988         
46989         this.inputEl().on("keypress", keyPress, this);
46990         
46991     },
46992     
46993     onTriggerClick : function(e)
46994     {   
46995         if(this.disabled){
46996             return;
46997         }
46998         
46999         this.page = 0;
47000         this.loadNext = false;
47001         
47002         if(this.isExpanded()){
47003             this.collapse();
47004             return;
47005         }
47006         
47007         this.hasFocus = true;
47008         
47009         if(this.triggerAction == 'all') {
47010             this.doQuery(this.allQuery, true);
47011             return;
47012         }
47013         
47014         this.doQuery(this.getRawValue());
47015     },
47016     
47017     getCurrency : function()
47018     {   
47019         var v = this.currencyEl().getValue();
47020         
47021         return v;
47022     },
47023     
47024     restrictHeight : function()
47025     {
47026         this.list.alignTo(this.currencyEl(), this.listAlign);
47027         this.list.alignTo(this.currencyEl(), this.listAlign);
47028     },
47029     
47030     onViewClick : function(view, doFocus, el, e)
47031     {
47032         var index = this.view.getSelectedIndexes()[0];
47033         
47034         var r = this.store.getAt(index);
47035         
47036         if(r){
47037             this.onSelect(r, index);
47038         }
47039     },
47040     
47041     onSelect : function(record, index){
47042         
47043         if(this.fireEvent('beforeselect', this, record, index) !== false){
47044         
47045             this.setFromCurrencyData(index > -1 ? record.data : false);
47046             
47047             this.collapse();
47048             
47049             this.fireEvent('select', this, record, index);
47050         }
47051     },
47052     
47053     setFromCurrencyData : function(o)
47054     {
47055         var currency = '';
47056         
47057         this.lastCurrency = o;
47058         
47059         if (this.currencyField) {
47060             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
47061         } else {
47062             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
47063         }
47064         
47065         this.lastSelectionText = currency;
47066         
47067         //setting default currency
47068         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
47069             this.setCurrency(this.defaultCurrency);
47070             return;
47071         }
47072         
47073         this.setCurrency(currency);
47074     },
47075     
47076     setFromData : function(o)
47077     {
47078         var c = {};
47079         
47080         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
47081         
47082         this.setFromCurrencyData(c);
47083         
47084         var value = '';
47085         
47086         if (this.name) {
47087             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
47088         } else {
47089             Roo.log('no value set for '+ (this.name ? this.name : this.id));
47090         }
47091         
47092         this.setValue(value);
47093         
47094     },
47095     
47096     setCurrency : function(v)
47097     {   
47098         this.currencyValue = v;
47099         
47100         if(this.rendered){
47101             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
47102             this.validate();
47103         }
47104     },
47105     
47106     setValue : function(v)
47107     {
47108         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
47109         
47110         this.value = v;
47111         
47112         if(this.rendered){
47113             
47114             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
47115             
47116             this.inputEl().dom.value = (v == '') ? '' :
47117                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
47118             
47119             if(!this.allowZero && v === '0') {
47120                 this.hiddenEl().dom.value = '';
47121                 this.inputEl().dom.value = '';
47122             }
47123             
47124             this.validate();
47125         }
47126     },
47127     
47128     getRawValue : function()
47129     {
47130         var v = this.inputEl().getValue();
47131         
47132         return v;
47133     },
47134     
47135     getValue : function()
47136     {
47137         return this.fixPrecision(this.parseValue(this.getRawValue()));
47138     },
47139     
47140     parseValue : function(value)
47141     {
47142         if(this.thousandsDelimiter) {
47143             value += "";
47144             r = new RegExp(",", "g");
47145             value = value.replace(r, "");
47146         }
47147         
47148         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
47149         return isNaN(value) ? '' : value;
47150         
47151     },
47152     
47153     fixPrecision : function(value)
47154     {
47155         if(this.thousandsDelimiter) {
47156             value += "";
47157             r = new RegExp(",", "g");
47158             value = value.replace(r, "");
47159         }
47160         
47161         var nan = isNaN(value);
47162         
47163         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
47164             return nan ? '' : value;
47165         }
47166         return parseFloat(value).toFixed(this.decimalPrecision);
47167     },
47168     
47169     decimalPrecisionFcn : function(v)
47170     {
47171         return Math.floor(v);
47172     },
47173     
47174     validateValue : function(value)
47175     {
47176         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
47177             return false;
47178         }
47179         
47180         var num = this.parseValue(value);
47181         
47182         if(isNaN(num)){
47183             this.markInvalid(String.format(this.nanText, value));
47184             return false;
47185         }
47186         
47187         if(num < this.minValue){
47188             this.markInvalid(String.format(this.minText, this.minValue));
47189             return false;
47190         }
47191         
47192         if(num > this.maxValue){
47193             this.markInvalid(String.format(this.maxText, this.maxValue));
47194             return false;
47195         }
47196         
47197         return true;
47198     },
47199     
47200     validate : function()
47201     {
47202         if(this.disabled || this.allowBlank){
47203             this.markValid();
47204             return true;
47205         }
47206         
47207         var currency = this.getCurrency();
47208         
47209         if(this.validateValue(this.getRawValue()) && currency.length){
47210             this.markValid();
47211             return true;
47212         }
47213         
47214         this.markInvalid();
47215         return false;
47216     },
47217     
47218     getName: function()
47219     {
47220         return this.name;
47221     },
47222     
47223     beforeBlur : function()
47224     {
47225         if(!this.castInt){
47226             return;
47227         }
47228         
47229         var v = this.parseValue(this.getRawValue());
47230         
47231         if(v || v == 0){
47232             this.setValue(v);
47233         }
47234     },
47235     
47236     onBlur : function()
47237     {
47238         this.beforeBlur();
47239         
47240         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
47241             //this.el.removeClass(this.focusClass);
47242         }
47243         
47244         this.hasFocus = false;
47245         
47246         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
47247             this.validate();
47248         }
47249         
47250         var v = this.getValue();
47251         
47252         if(String(v) !== String(this.startValue)){
47253             this.fireEvent('change', this, v, this.startValue);
47254         }
47255         
47256         this.fireEvent("blur", this);
47257     },
47258     
47259     inputEl : function()
47260     {
47261         return this.el.select('.roo-money-amount-input', true).first();
47262     },
47263     
47264     currencyEl : function()
47265     {
47266         return this.el.select('.roo-money-currency-input', true).first();
47267     },
47268     
47269     hiddenEl : function()
47270     {
47271         return this.el.select('input.hidden-number-input',true).first();
47272     }
47273     
47274 });/**
47275  * @class Roo.bootstrap.BezierSignature
47276  * @extends Roo.bootstrap.Component
47277  * Bootstrap BezierSignature class
47278  * This script refer to:
47279  *    Title: Signature Pad
47280  *    Author: szimek
47281  *    Availability: https://github.com/szimek/signature_pad
47282  *
47283  * @constructor
47284  * Create a new BezierSignature
47285  * @param {Object} config The config object
47286  */
47287
47288 Roo.bootstrap.BezierSignature = function(config){
47289     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
47290     this.addEvents({
47291         "resize" : true
47292     });
47293 };
47294
47295 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
47296 {
47297      
47298     curve_data: [],
47299     
47300     is_empty: true,
47301     
47302     mouse_btn_down: true,
47303     
47304     /**
47305      * @cfg {int} canvas height
47306      */
47307     canvas_height: '200px',
47308     
47309     /**
47310      * @cfg {float|function} Radius of a single dot.
47311      */ 
47312     dot_size: false,
47313     
47314     /**
47315      * @cfg {float} Minimum width of a line. Defaults to 0.5.
47316      */
47317     min_width: 0.5,
47318     
47319     /**
47320      * @cfg {float} Maximum width of a line. Defaults to 2.5.
47321      */
47322     max_width: 2.5,
47323     
47324     /**
47325      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
47326      */
47327     throttle: 16,
47328     
47329     /**
47330      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
47331      */
47332     min_distance: 5,
47333     
47334     /**
47335      * @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.
47336      */
47337     bg_color: 'rgba(0, 0, 0, 0)',
47338     
47339     /**
47340      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
47341      */
47342     dot_color: 'black',
47343     
47344     /**
47345      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
47346      */ 
47347     velocity_filter_weight: 0.7,
47348     
47349     /**
47350      * @cfg {function} Callback when stroke begin. 
47351      */
47352     onBegin: false,
47353     
47354     /**
47355      * @cfg {function} Callback when stroke end.
47356      */
47357     onEnd: false,
47358     
47359     getAutoCreate : function()
47360     {
47361         var cls = 'roo-signature column';
47362         
47363         if(this.cls){
47364             cls += ' ' + this.cls;
47365         }
47366         
47367         var col_sizes = [
47368             'lg',
47369             'md',
47370             'sm',
47371             'xs'
47372         ];
47373         
47374         for(var i = 0; i < col_sizes.length; i++) {
47375             if(this[col_sizes[i]]) {
47376                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
47377             }
47378         }
47379         
47380         var cfg = {
47381             tag: 'div',
47382             cls: cls,
47383             cn: [
47384                 {
47385                     tag: 'div',
47386                     cls: 'roo-signature-body',
47387                     cn: [
47388                         {
47389                             tag: 'canvas',
47390                             cls: 'roo-signature-body-canvas',
47391                             height: this.canvas_height,
47392                             width: this.canvas_width
47393                         }
47394                     ]
47395                 },
47396                 {
47397                     tag: 'input',
47398                     type: 'file',
47399                     style: 'display: none'
47400                 }
47401             ]
47402         };
47403         
47404         return cfg;
47405     },
47406     
47407     initEvents: function() 
47408     {
47409         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
47410         
47411         var canvas = this.canvasEl();
47412         
47413         // mouse && touch event swapping...
47414         canvas.dom.style.touchAction = 'none';
47415         canvas.dom.style.msTouchAction = 'none';
47416         
47417         this.mouse_btn_down = false;
47418         canvas.on('mousedown', this._handleMouseDown, this);
47419         canvas.on('mousemove', this._handleMouseMove, this);
47420         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
47421         
47422         if (window.PointerEvent) {
47423             canvas.on('pointerdown', this._handleMouseDown, this);
47424             canvas.on('pointermove', this._handleMouseMove, this);
47425             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
47426         }
47427         
47428         if ('ontouchstart' in window) {
47429             canvas.on('touchstart', this._handleTouchStart, this);
47430             canvas.on('touchmove', this._handleTouchMove, this);
47431             canvas.on('touchend', this._handleTouchEnd, this);
47432         }
47433         
47434         Roo.EventManager.onWindowResize(this.resize, this, true);
47435         
47436         // file input event
47437         this.fileEl().on('change', this.uploadImage, this);
47438         
47439         this.clear();
47440         
47441         this.resize();
47442     },
47443     
47444     resize: function(){
47445         
47446         var canvas = this.canvasEl().dom;
47447         var ctx = this.canvasElCtx();
47448         var img_data = false;
47449         
47450         if(canvas.width > 0) {
47451             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
47452         }
47453         // setting canvas width will clean img data
47454         canvas.width = 0;
47455         
47456         var style = window.getComputedStyle ? 
47457             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
47458             
47459         var padding_left = parseInt(style.paddingLeft) || 0;
47460         var padding_right = parseInt(style.paddingRight) || 0;
47461         
47462         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
47463         
47464         if(img_data) {
47465             ctx.putImageData(img_data, 0, 0);
47466         }
47467     },
47468     
47469     _handleMouseDown: function(e)
47470     {
47471         if (e.browserEvent.which === 1) {
47472             this.mouse_btn_down = true;
47473             this.strokeBegin(e);
47474         }
47475     },
47476     
47477     _handleMouseMove: function (e)
47478     {
47479         if (this.mouse_btn_down) {
47480             this.strokeMoveUpdate(e);
47481         }
47482     },
47483     
47484     _handleMouseUp: function (e)
47485     {
47486         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
47487             this.mouse_btn_down = false;
47488             this.strokeEnd(e);
47489         }
47490     },
47491     
47492     _handleTouchStart: function (e) {
47493         
47494         e.preventDefault();
47495         if (e.browserEvent.targetTouches.length === 1) {
47496             // var touch = e.browserEvent.changedTouches[0];
47497             // this.strokeBegin(touch);
47498             
47499              this.strokeBegin(e); // assume e catching the correct xy...
47500         }
47501     },
47502     
47503     _handleTouchMove: function (e) {
47504         e.preventDefault();
47505         // var touch = event.targetTouches[0];
47506         // _this._strokeMoveUpdate(touch);
47507         this.strokeMoveUpdate(e);
47508     },
47509     
47510     _handleTouchEnd: function (e) {
47511         var wasCanvasTouched = e.target === this.canvasEl().dom;
47512         if (wasCanvasTouched) {
47513             e.preventDefault();
47514             // var touch = event.changedTouches[0];
47515             // _this._strokeEnd(touch);
47516             this.strokeEnd(e);
47517         }
47518     },
47519     
47520     reset: function () {
47521         this._lastPoints = [];
47522         this._lastVelocity = 0;
47523         this._lastWidth = (this.min_width + this.max_width) / 2;
47524         this.canvasElCtx().fillStyle = this.dot_color;
47525     },
47526     
47527     strokeMoveUpdate: function(e)
47528     {
47529         this.strokeUpdate(e);
47530         
47531         if (this.throttle) {
47532             this.throttleStroke(this.strokeUpdate, this.throttle);
47533         }
47534         else {
47535             this.strokeUpdate(e);
47536         }
47537     },
47538     
47539     strokeBegin: function(e)
47540     {
47541         var newPointGroup = {
47542             color: this.dot_color,
47543             points: []
47544         };
47545         
47546         if (typeof this.onBegin === 'function') {
47547             this.onBegin(e);
47548         }
47549         
47550         this.curve_data.push(newPointGroup);
47551         this.reset();
47552         this.strokeUpdate(e);
47553     },
47554     
47555     strokeUpdate: function(e)
47556     {
47557         var rect = this.canvasEl().dom.getBoundingClientRect();
47558         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
47559         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
47560         var lastPoints = lastPointGroup.points;
47561         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
47562         var isLastPointTooClose = lastPoint
47563             ? point.distanceTo(lastPoint) <= this.min_distance
47564             : false;
47565         var color = lastPointGroup.color;
47566         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
47567             var curve = this.addPoint(point);
47568             if (!lastPoint) {
47569                 this.drawDot({color: color, point: point});
47570             }
47571             else if (curve) {
47572                 this.drawCurve({color: color, curve: curve});
47573             }
47574             lastPoints.push({
47575                 time: point.time,
47576                 x: point.x,
47577                 y: point.y
47578             });
47579         }
47580     },
47581     
47582     strokeEnd: function(e)
47583     {
47584         this.strokeUpdate(e);
47585         if (typeof this.onEnd === 'function') {
47586             this.onEnd(e);
47587         }
47588     },
47589     
47590     addPoint:  function (point) {
47591         var _lastPoints = this._lastPoints;
47592         _lastPoints.push(point);
47593         if (_lastPoints.length > 2) {
47594             if (_lastPoints.length === 3) {
47595                 _lastPoints.unshift(_lastPoints[0]);
47596             }
47597             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
47598             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
47599             _lastPoints.shift();
47600             return curve;
47601         }
47602         return null;
47603     },
47604     
47605     calculateCurveWidths: function (startPoint, endPoint) {
47606         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
47607             (1 - this.velocity_filter_weight) * this._lastVelocity;
47608
47609         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
47610         var widths = {
47611             end: newWidth,
47612             start: this._lastWidth
47613         };
47614         
47615         this._lastVelocity = velocity;
47616         this._lastWidth = newWidth;
47617         return widths;
47618     },
47619     
47620     drawDot: function (_a) {
47621         var color = _a.color, point = _a.point;
47622         var ctx = this.canvasElCtx();
47623         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
47624         ctx.beginPath();
47625         this.drawCurveSegment(point.x, point.y, width);
47626         ctx.closePath();
47627         ctx.fillStyle = color;
47628         ctx.fill();
47629     },
47630     
47631     drawCurve: function (_a) {
47632         var color = _a.color, curve = _a.curve;
47633         var ctx = this.canvasElCtx();
47634         var widthDelta = curve.endWidth - curve.startWidth;
47635         var drawSteps = Math.floor(curve.length()) * 2;
47636         ctx.beginPath();
47637         ctx.fillStyle = color;
47638         for (var i = 0; i < drawSteps; i += 1) {
47639         var t = i / drawSteps;
47640         var tt = t * t;
47641         var ttt = tt * t;
47642         var u = 1 - t;
47643         var uu = u * u;
47644         var uuu = uu * u;
47645         var x = uuu * curve.startPoint.x;
47646         x += 3 * uu * t * curve.control1.x;
47647         x += 3 * u * tt * curve.control2.x;
47648         x += ttt * curve.endPoint.x;
47649         var y = uuu * curve.startPoint.y;
47650         y += 3 * uu * t * curve.control1.y;
47651         y += 3 * u * tt * curve.control2.y;
47652         y += ttt * curve.endPoint.y;
47653         var width = curve.startWidth + ttt * widthDelta;
47654         this.drawCurveSegment(x, y, width);
47655         }
47656         ctx.closePath();
47657         ctx.fill();
47658     },
47659     
47660     drawCurveSegment: function (x, y, width) {
47661         var ctx = this.canvasElCtx();
47662         ctx.moveTo(x, y);
47663         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
47664         this.is_empty = false;
47665     },
47666     
47667     clear: function()
47668     {
47669         var ctx = this.canvasElCtx();
47670         var canvas = this.canvasEl().dom;
47671         ctx.fillStyle = this.bg_color;
47672         ctx.clearRect(0, 0, canvas.width, canvas.height);
47673         ctx.fillRect(0, 0, canvas.width, canvas.height);
47674         this.curve_data = [];
47675         this.reset();
47676         this.is_empty = true;
47677     },
47678     
47679     fileEl: function()
47680     {
47681         return  this.el.select('input',true).first();
47682     },
47683     
47684     canvasEl: function()
47685     {
47686         return this.el.select('canvas',true).first();
47687     },
47688     
47689     canvasElCtx: function()
47690     {
47691         return this.el.select('canvas',true).first().dom.getContext('2d');
47692     },
47693     
47694     getImage: function(type)
47695     {
47696         if(this.is_empty) {
47697             return false;
47698         }
47699         
47700         // encryption ?
47701         return this.canvasEl().dom.toDataURL('image/'+type, 1);
47702     },
47703     
47704     drawFromImage: function(img_src)
47705     {
47706         var img = new Image();
47707         
47708         img.onload = function(){
47709             this.canvasElCtx().drawImage(img, 0, 0);
47710         }.bind(this);
47711         
47712         img.src = img_src;
47713         
47714         this.is_empty = false;
47715     },
47716     
47717     selectImage: function()
47718     {
47719         this.fileEl().dom.click();
47720     },
47721     
47722     uploadImage: function(e)
47723     {
47724         var reader = new FileReader();
47725         
47726         reader.onload = function(e){
47727             var img = new Image();
47728             img.onload = function(){
47729                 this.reset();
47730                 this.canvasElCtx().drawImage(img, 0, 0);
47731             }.bind(this);
47732             img.src = e.target.result;
47733         }.bind(this);
47734         
47735         reader.readAsDataURL(e.target.files[0]);
47736     },
47737     
47738     // Bezier Point Constructor
47739     Point: (function () {
47740         function Point(x, y, time) {
47741             this.x = x;
47742             this.y = y;
47743             this.time = time || Date.now();
47744         }
47745         Point.prototype.distanceTo = function (start) {
47746             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
47747         };
47748         Point.prototype.equals = function (other) {
47749             return this.x === other.x && this.y === other.y && this.time === other.time;
47750         };
47751         Point.prototype.velocityFrom = function (start) {
47752             return this.time !== start.time
47753             ? this.distanceTo(start) / (this.time - start.time)
47754             : 0;
47755         };
47756         return Point;
47757     }()),
47758     
47759     
47760     // Bezier Constructor
47761     Bezier: (function () {
47762         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
47763             this.startPoint = startPoint;
47764             this.control2 = control2;
47765             this.control1 = control1;
47766             this.endPoint = endPoint;
47767             this.startWidth = startWidth;
47768             this.endWidth = endWidth;
47769         }
47770         Bezier.fromPoints = function (points, widths, scope) {
47771             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
47772             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
47773             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
47774         };
47775         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
47776             var dx1 = s1.x - s2.x;
47777             var dy1 = s1.y - s2.y;
47778             var dx2 = s2.x - s3.x;
47779             var dy2 = s2.y - s3.y;
47780             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
47781             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
47782             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
47783             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
47784             var dxm = m1.x - m2.x;
47785             var dym = m1.y - m2.y;
47786             var k = l2 / (l1 + l2);
47787             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
47788             var tx = s2.x - cm.x;
47789             var ty = s2.y - cm.y;
47790             return {
47791                 c1: new scope.Point(m1.x + tx, m1.y + ty),
47792                 c2: new scope.Point(m2.x + tx, m2.y + ty)
47793             };
47794         };
47795         Bezier.prototype.length = function () {
47796             var steps = 10;
47797             var length = 0;
47798             var px;
47799             var py;
47800             for (var i = 0; i <= steps; i += 1) {
47801                 var t = i / steps;
47802                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
47803                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
47804                 if (i > 0) {
47805                     var xdiff = cx - px;
47806                     var ydiff = cy - py;
47807                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
47808                 }
47809                 px = cx;
47810                 py = cy;
47811             }
47812             return length;
47813         };
47814         Bezier.prototype.point = function (t, start, c1, c2, end) {
47815             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
47816             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
47817             + (3.0 * c2 * (1.0 - t) * t * t)
47818             + (end * t * t * t);
47819         };
47820         return Bezier;
47821     }()),
47822     
47823     throttleStroke: function(fn, wait) {
47824       if (wait === void 0) { wait = 250; }
47825       var previous = 0;
47826       var timeout = null;
47827       var result;
47828       var storedContext;
47829       var storedArgs;
47830       var later = function () {
47831           previous = Date.now();
47832           timeout = null;
47833           result = fn.apply(storedContext, storedArgs);
47834           if (!timeout) {
47835               storedContext = null;
47836               storedArgs = [];
47837           }
47838       };
47839       return function wrapper() {
47840           var args = [];
47841           for (var _i = 0; _i < arguments.length; _i++) {
47842               args[_i] = arguments[_i];
47843           }
47844           var now = Date.now();
47845           var remaining = wait - (now - previous);
47846           storedContext = this;
47847           storedArgs = args;
47848           if (remaining <= 0 || remaining > wait) {
47849               if (timeout) {
47850                   clearTimeout(timeout);
47851                   timeout = null;
47852               }
47853               previous = now;
47854               result = fn.apply(storedContext, storedArgs);
47855               if (!timeout) {
47856                   storedContext = null;
47857                   storedArgs = [];
47858               }
47859           }
47860           else if (!timeout) {
47861               timeout = window.setTimeout(later, remaining);
47862           }
47863           return result;
47864       };
47865   }
47866   
47867 });
47868
47869  
47870
47871  // old names for form elements
47872 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
47873 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
47874 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
47875 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
47876 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
47877 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
47878 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
47879 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
47880 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
47881 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
47882 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
47883 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
47884 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
47885 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
47886 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
47887 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
47888 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
47889 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
47890 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
47891 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
47892 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
47893 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
47894 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
47895 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
47896 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
47897 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
47898
47899 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
47900 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
47901
47902 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
47903 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
47904
47905 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
47906 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
47907 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
47908 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
47909