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         console.log(ct);
33522         console.log(position);
33523         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
33524         
33525         if (this.buttons.length) {
33526             
33527             Roo.each(this.buttons, function(bb) {
33528                 
33529                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
33530                 
33531                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
33532                 
33533             }, this);
33534         }
33535         
33536         if(this.loadMask){
33537             this.maskEl = this.el;
33538         }
33539     },
33540     
33541     initEvents : function()
33542     {
33543         this.urlAPI = (window.createObjectURL && window) || 
33544                                 (window.URL && URL.revokeObjectURL && URL) || 
33545                                 (window.webkitURL && webkitURL);
33546                         
33547         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
33548         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33549         
33550         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
33551         this.selectorEl.hide();
33552         
33553         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
33554         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33555         
33556         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
33557         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33558         this.thumbEl.hide();
33559         
33560         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
33561         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33562         
33563         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
33564         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33565         this.errorEl.hide();
33566         
33567         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
33568         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33569         this.footerEl.hide();
33570         
33571         this.setThumbBoxSize();
33572         
33573         this.bind();
33574         
33575         this.resize();
33576         
33577         this.fireEvent('initial', this);
33578     },
33579
33580     bind : function()
33581     {
33582         var _this = this;
33583         
33584         window.addEventListener("resize", function() { _this.resize(); } );
33585         
33586         this.bodyEl.on('click', this.beforeSelectFile, this);
33587         
33588         if(Roo.isTouch){
33589             this.bodyEl.on('touchstart', this.onTouchStart, this);
33590             this.bodyEl.on('touchmove', this.onTouchMove, this);
33591             this.bodyEl.on('touchend', this.onTouchEnd, this);
33592         }
33593         
33594         if(!Roo.isTouch){
33595             this.bodyEl.on('mousedown', this.onMouseDown, this);
33596             this.bodyEl.on('mousemove', this.onMouseMove, this);
33597             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
33598             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
33599             Roo.get(document).on('mouseup', this.onMouseUp, this);
33600         }
33601         
33602         this.selectorEl.on('change', this.onFileSelected, this);
33603     },
33604     
33605     reset : function()
33606     {    
33607         this.scale = 0;
33608         this.baseScale = 1;
33609         this.rotate = 0;
33610         this.baseRotate = 1;
33611         this.dragable = false;
33612         this.pinching = false;
33613         this.mouseX = 0;
33614         this.mouseY = 0;
33615         this.cropData = false;
33616         this.notifyEl.dom.innerHTML = this.emptyText;
33617         
33618         this.selectorEl.dom.value = '';
33619         
33620     },
33621     
33622     resize : function()
33623     {
33624         if(this.fireEvent('resize', this) != false){
33625             this.setThumbBoxPosition();
33626             this.setCanvasPosition();
33627         }
33628     },
33629     
33630     onFooterButtonClick : function(e, el, o, type)
33631     {
33632         switch (type) {
33633             case 'rotate-left' :
33634                 this.onRotateLeft(e);
33635                 break;
33636             case 'rotate-right' :
33637                 this.onRotateRight(e);
33638                 break;
33639             case 'picture' :
33640                 this.beforeSelectFile(e);
33641                 break;
33642             case 'trash' :
33643                 this.trash(e);
33644                 break;
33645             case 'crop' :
33646                 this.crop(e);
33647                 break;
33648             case 'download' :
33649                 this.download(e);
33650                 break;
33651             default :
33652                 break;
33653         }
33654         
33655         this.fireEvent('footerbuttonclick', this, type);
33656     },
33657     
33658     beforeSelectFile : function(e)
33659     {
33660         e.preventDefault();
33661         
33662         if(this.fireEvent('beforeselectfile', this) != false){
33663             this.selectorEl.dom.click();
33664         }
33665     },
33666     
33667     onFileSelected : function(e)
33668     {
33669         e.preventDefault();
33670         
33671         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
33672             return;
33673         }
33674         
33675         var file = this.selectorEl.dom.files[0];
33676         
33677         if(this.fireEvent('inspect', this, file) != false){
33678             this.prepare(file);
33679         }
33680         
33681     },
33682     
33683     trash : function(e)
33684     {
33685         this.fireEvent('trash', this);
33686     },
33687     
33688     download : function(e)
33689     {
33690         this.fireEvent('download', this);
33691     },
33692     
33693     loadCanvas : function(src)
33694     {   
33695         if(this.fireEvent('beforeloadcanvas', this, src) != false){
33696             
33697             this.reset();
33698             
33699             this.imageEl = document.createElement('img');
33700             
33701             var _this = this;
33702             
33703             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
33704             
33705             this.imageEl.src = src;
33706         }
33707     },
33708     
33709     onLoadCanvas : function()
33710     {   
33711         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
33712         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
33713         
33714         this.bodyEl.un('click', this.beforeSelectFile, this);
33715         
33716         this.notifyEl.hide();
33717         this.thumbEl.show();
33718         this.footerEl.show();
33719         
33720         this.baseRotateLevel();
33721         
33722         if(this.isDocument){
33723             this.setThumbBoxSize();
33724         }
33725         
33726         this.setThumbBoxPosition();
33727         
33728         this.baseScaleLevel();
33729         
33730         this.draw();
33731         
33732         this.resize();
33733         
33734         this.canvasLoaded = true;
33735         
33736         if(this.loadMask){
33737             this.maskEl.unmask();
33738         }
33739         
33740     },
33741     
33742     setCanvasPosition : function()
33743     {   
33744         if(!this.canvasEl){
33745             return;
33746         }
33747         
33748         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
33749         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
33750         
33751         this.previewEl.setLeft(pw);
33752         this.previewEl.setTop(ph);
33753         
33754     },
33755     
33756     onMouseDown : function(e)
33757     {   
33758         e.stopEvent();
33759         
33760         this.dragable = true;
33761         this.pinching = false;
33762         
33763         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
33764             this.dragable = false;
33765             return;
33766         }
33767         
33768         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33769         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33770         
33771     },
33772     
33773     onMouseMove : function(e)
33774     {   
33775         e.stopEvent();
33776         
33777         if(!this.canvasLoaded){
33778             return;
33779         }
33780         
33781         if (!this.dragable){
33782             return;
33783         }
33784         
33785         var minX = Math.ceil(this.thumbEl.getLeft(true));
33786         var minY = Math.ceil(this.thumbEl.getTop(true));
33787         
33788         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
33789         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
33790         
33791         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33792         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33793         
33794         x = x - this.mouseX;
33795         y = y - this.mouseY;
33796         
33797         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
33798         var bgY = Math.ceil(y + this.previewEl.getTop(true));
33799         
33800         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
33801         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
33802         
33803         this.previewEl.setLeft(bgX);
33804         this.previewEl.setTop(bgY);
33805         
33806         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33807         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33808     },
33809     
33810     onMouseUp : function(e)
33811     {   
33812         e.stopEvent();
33813         
33814         this.dragable = false;
33815     },
33816     
33817     onMouseWheel : function(e)
33818     {   
33819         e.stopEvent();
33820         
33821         this.startScale = this.scale;
33822         
33823         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
33824         
33825         if(!this.zoomable()){
33826             this.scale = this.startScale;
33827             return;
33828         }
33829         
33830         this.draw();
33831         
33832         return;
33833     },
33834     
33835     zoomable : function()
33836     {
33837         var minScale = this.thumbEl.getWidth() / this.minWidth;
33838         
33839         if(this.minWidth < this.minHeight){
33840             minScale = this.thumbEl.getHeight() / this.minHeight;
33841         }
33842         
33843         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
33844         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
33845         
33846         if(
33847                 this.isDocument &&
33848                 (this.rotate == 0 || this.rotate == 180) && 
33849                 (
33850                     width > this.imageEl.OriginWidth || 
33851                     height > this.imageEl.OriginHeight ||
33852                     (width < this.minWidth && height < this.minHeight)
33853                 )
33854         ){
33855             return false;
33856         }
33857         
33858         if(
33859                 this.isDocument &&
33860                 (this.rotate == 90 || this.rotate == 270) && 
33861                 (
33862                     width > this.imageEl.OriginWidth || 
33863                     height > this.imageEl.OriginHeight ||
33864                     (width < this.minHeight && height < this.minWidth)
33865                 )
33866         ){
33867             return false;
33868         }
33869         
33870         if(
33871                 !this.isDocument &&
33872                 (this.rotate == 0 || this.rotate == 180) && 
33873                 (
33874                     width < this.minWidth || 
33875                     width > this.imageEl.OriginWidth || 
33876                     height < this.minHeight || 
33877                     height > this.imageEl.OriginHeight
33878                 )
33879         ){
33880             return false;
33881         }
33882         
33883         if(
33884                 !this.isDocument &&
33885                 (this.rotate == 90 || this.rotate == 270) && 
33886                 (
33887                     width < this.minHeight || 
33888                     width > this.imageEl.OriginWidth || 
33889                     height < this.minWidth || 
33890                     height > this.imageEl.OriginHeight
33891                 )
33892         ){
33893             return false;
33894         }
33895         
33896         return true;
33897         
33898     },
33899     
33900     onRotateLeft : function(e)
33901     {   
33902         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
33903             
33904             var minScale = this.thumbEl.getWidth() / this.minWidth;
33905             
33906             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
33907             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
33908             
33909             this.startScale = this.scale;
33910             
33911             while (this.getScaleLevel() < minScale){
33912             
33913                 this.scale = this.scale + 1;
33914                 
33915                 if(!this.zoomable()){
33916                     break;
33917                 }
33918                 
33919                 if(
33920                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
33921                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
33922                 ){
33923                     continue;
33924                 }
33925                 
33926                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
33927
33928                 this.draw();
33929                 
33930                 return;
33931             }
33932             
33933             this.scale = this.startScale;
33934             
33935             this.onRotateFail();
33936             
33937             return false;
33938         }
33939         
33940         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
33941
33942         if(this.isDocument){
33943             this.setThumbBoxSize();
33944             this.setThumbBoxPosition();
33945             this.setCanvasPosition();
33946         }
33947         
33948         this.draw();
33949         
33950         this.fireEvent('rotate', this, 'left');
33951         
33952     },
33953     
33954     onRotateRight : function(e)
33955     {
33956         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
33957             
33958             var minScale = this.thumbEl.getWidth() / this.minWidth;
33959         
33960             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
33961             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
33962             
33963             this.startScale = this.scale;
33964             
33965             while (this.getScaleLevel() < minScale){
33966             
33967                 this.scale = this.scale + 1;
33968                 
33969                 if(!this.zoomable()){
33970                     break;
33971                 }
33972                 
33973                 if(
33974                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
33975                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
33976                 ){
33977                     continue;
33978                 }
33979                 
33980                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
33981
33982                 this.draw();
33983                 
33984                 return;
33985             }
33986             
33987             this.scale = this.startScale;
33988             
33989             this.onRotateFail();
33990             
33991             return false;
33992         }
33993         
33994         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
33995
33996         if(this.isDocument){
33997             this.setThumbBoxSize();
33998             this.setThumbBoxPosition();
33999             this.setCanvasPosition();
34000         }
34001         
34002         this.draw();
34003         
34004         this.fireEvent('rotate', this, 'right');
34005     },
34006     
34007     onRotateFail : function()
34008     {
34009         this.errorEl.show(true);
34010         
34011         var _this = this;
34012         
34013         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
34014     },
34015     
34016     draw : function()
34017     {
34018         this.previewEl.dom.innerHTML = '';
34019         
34020         var canvasEl = document.createElement("canvas");
34021         
34022         var contextEl = canvasEl.getContext("2d");
34023         
34024         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34025         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34026         var center = this.imageEl.OriginWidth / 2;
34027         
34028         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
34029             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34030             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34031             center = this.imageEl.OriginHeight / 2;
34032         }
34033         
34034         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
34035         
34036         contextEl.translate(center, center);
34037         contextEl.rotate(this.rotate * Math.PI / 180);
34038
34039         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34040         
34041         this.canvasEl = document.createElement("canvas");
34042         
34043         this.contextEl = this.canvasEl.getContext("2d");
34044         
34045         switch (this.rotate) {
34046             case 0 :
34047                 
34048                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34049                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34050                 
34051                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34052                 
34053                 break;
34054             case 90 : 
34055                 
34056                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34057                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34058                 
34059                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34060                     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);
34061                     break;
34062                 }
34063                 
34064                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34065                 
34066                 break;
34067             case 180 :
34068                 
34069                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34070                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34071                 
34072                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34073                     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);
34074                     break;
34075                 }
34076                 
34077                 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);
34078                 
34079                 break;
34080             case 270 :
34081                 
34082                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34083                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34084         
34085                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34086                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34087                     break;
34088                 }
34089                 
34090                 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);
34091                 
34092                 break;
34093             default : 
34094                 break;
34095         }
34096         
34097         this.previewEl.appendChild(this.canvasEl);
34098         
34099         this.setCanvasPosition();
34100     },
34101     
34102     crop : function()
34103     {
34104         if(!this.canvasLoaded){
34105             return;
34106         }
34107         
34108         var imageCanvas = document.createElement("canvas");
34109         
34110         var imageContext = imageCanvas.getContext("2d");
34111         
34112         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34113         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34114         
34115         var center = imageCanvas.width / 2;
34116         
34117         imageContext.translate(center, center);
34118         
34119         imageContext.rotate(this.rotate * Math.PI / 180);
34120         
34121         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34122         
34123         var canvas = document.createElement("canvas");
34124         
34125         var context = canvas.getContext("2d");
34126                 
34127         canvas.width = this.minWidth;
34128         canvas.height = this.minHeight;
34129
34130         switch (this.rotate) {
34131             case 0 :
34132                 
34133                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34134                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34135                 
34136                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34137                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34138                 
34139                 var targetWidth = this.minWidth - 2 * x;
34140                 var targetHeight = this.minHeight - 2 * y;
34141                 
34142                 var scale = 1;
34143                 
34144                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34145                     scale = targetWidth / width;
34146                 }
34147                 
34148                 if(x > 0 && y == 0){
34149                     scale = targetHeight / height;
34150                 }
34151                 
34152                 if(x > 0 && y > 0){
34153                     scale = targetWidth / width;
34154                     
34155                     if(width < height){
34156                         scale = targetHeight / height;
34157                     }
34158                 }
34159                 
34160                 context.scale(scale, scale);
34161                 
34162                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34163                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34164
34165                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34166                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34167
34168                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34169                 
34170                 break;
34171             case 90 : 
34172                 
34173                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34174                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34175                 
34176                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34177                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34178                 
34179                 var targetWidth = this.minWidth - 2 * x;
34180                 var targetHeight = this.minHeight - 2 * y;
34181                 
34182                 var scale = 1;
34183                 
34184                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34185                     scale = targetWidth / width;
34186                 }
34187                 
34188                 if(x > 0 && y == 0){
34189                     scale = targetHeight / height;
34190                 }
34191                 
34192                 if(x > 0 && y > 0){
34193                     scale = targetWidth / width;
34194                     
34195                     if(width < height){
34196                         scale = targetHeight / height;
34197                     }
34198                 }
34199                 
34200                 context.scale(scale, scale);
34201                 
34202                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34203                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34204
34205                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34206                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34207                 
34208                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34209                 
34210                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34211                 
34212                 break;
34213             case 180 :
34214                 
34215                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34216                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34217                 
34218                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34219                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34220                 
34221                 var targetWidth = this.minWidth - 2 * x;
34222                 var targetHeight = this.minHeight - 2 * y;
34223                 
34224                 var scale = 1;
34225                 
34226                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34227                     scale = targetWidth / width;
34228                 }
34229                 
34230                 if(x > 0 && y == 0){
34231                     scale = targetHeight / height;
34232                 }
34233                 
34234                 if(x > 0 && y > 0){
34235                     scale = targetWidth / width;
34236                     
34237                     if(width < height){
34238                         scale = targetHeight / height;
34239                     }
34240                 }
34241                 
34242                 context.scale(scale, scale);
34243                 
34244                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34245                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34246
34247                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34248                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34249
34250                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34251                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34252                 
34253                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34254                 
34255                 break;
34256             case 270 :
34257                 
34258                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34259                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34260                 
34261                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34262                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34263                 
34264                 var targetWidth = this.minWidth - 2 * x;
34265                 var targetHeight = this.minHeight - 2 * y;
34266                 
34267                 var scale = 1;
34268                 
34269                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34270                     scale = targetWidth / width;
34271                 }
34272                 
34273                 if(x > 0 && y == 0){
34274                     scale = targetHeight / height;
34275                 }
34276                 
34277                 if(x > 0 && y > 0){
34278                     scale = targetWidth / width;
34279                     
34280                     if(width < height){
34281                         scale = targetHeight / height;
34282                     }
34283                 }
34284                 
34285                 context.scale(scale, scale);
34286                 
34287                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34288                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34289
34290                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34291                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34292                 
34293                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34294                 
34295                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34296                 
34297                 break;
34298             default : 
34299                 break;
34300         }
34301         
34302         this.cropData = canvas.toDataURL(this.cropType);
34303         
34304         if(this.fireEvent('crop', this, this.cropData) !== false){
34305             this.process(this.file, this.cropData);
34306         }
34307         
34308         return;
34309         
34310     },
34311     
34312     setThumbBoxSize : function()
34313     {
34314         var width, height;
34315         
34316         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
34317             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
34318             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
34319             
34320             this.minWidth = width;
34321             this.minHeight = height;
34322             
34323             if(this.rotate == 90 || this.rotate == 270){
34324                 this.minWidth = height;
34325                 this.minHeight = width;
34326             }
34327         }
34328         
34329         height = 300;
34330         width = Math.ceil(this.minWidth * height / this.minHeight);
34331         
34332         if(this.minWidth > this.minHeight){
34333             width = 300;
34334             height = Math.ceil(this.minHeight * width / this.minWidth);
34335         }
34336         
34337         this.thumbEl.setStyle({
34338             width : width + 'px',
34339             height : height + 'px'
34340         });
34341
34342         return;
34343             
34344     },
34345     
34346     setThumbBoxPosition : function()
34347     {
34348         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
34349         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
34350         
34351         this.thumbEl.setLeft(x);
34352         this.thumbEl.setTop(y);
34353         
34354     },
34355     
34356     baseRotateLevel : function()
34357     {
34358         this.baseRotate = 1;
34359         
34360         if(
34361                 typeof(this.exif) != 'undefined' &&
34362                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
34363                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
34364         ){
34365             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
34366         }
34367         
34368         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
34369         
34370     },
34371     
34372     baseScaleLevel : function()
34373     {
34374         var width, height;
34375         
34376         if(this.isDocument){
34377             
34378             if(this.baseRotate == 6 || this.baseRotate == 8){
34379             
34380                 height = this.thumbEl.getHeight();
34381                 this.baseScale = height / this.imageEl.OriginWidth;
34382
34383                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
34384                     width = this.thumbEl.getWidth();
34385                     this.baseScale = width / this.imageEl.OriginHeight;
34386                 }
34387
34388                 return;
34389             }
34390
34391             height = this.thumbEl.getHeight();
34392             this.baseScale = height / this.imageEl.OriginHeight;
34393
34394             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
34395                 width = this.thumbEl.getWidth();
34396                 this.baseScale = width / this.imageEl.OriginWidth;
34397             }
34398
34399             return;
34400         }
34401         
34402         if(this.baseRotate == 6 || this.baseRotate == 8){
34403             
34404             width = this.thumbEl.getHeight();
34405             this.baseScale = width / this.imageEl.OriginHeight;
34406             
34407             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
34408                 height = this.thumbEl.getWidth();
34409                 this.baseScale = height / this.imageEl.OriginHeight;
34410             }
34411             
34412             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34413                 height = this.thumbEl.getWidth();
34414                 this.baseScale = height / this.imageEl.OriginHeight;
34415                 
34416                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
34417                     width = this.thumbEl.getHeight();
34418                     this.baseScale = width / this.imageEl.OriginWidth;
34419                 }
34420             }
34421             
34422             return;
34423         }
34424         
34425         width = this.thumbEl.getWidth();
34426         this.baseScale = width / this.imageEl.OriginWidth;
34427         
34428         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
34429             height = this.thumbEl.getHeight();
34430             this.baseScale = height / this.imageEl.OriginHeight;
34431         }
34432         
34433         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34434             
34435             height = this.thumbEl.getHeight();
34436             this.baseScale = height / this.imageEl.OriginHeight;
34437             
34438             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
34439                 width = this.thumbEl.getWidth();
34440                 this.baseScale = width / this.imageEl.OriginWidth;
34441             }
34442             
34443         }
34444         
34445         return;
34446     },
34447     
34448     getScaleLevel : function()
34449     {
34450         return this.baseScale * Math.pow(1.1, this.scale);
34451     },
34452     
34453     onTouchStart : function(e)
34454     {
34455         if(!this.canvasLoaded){
34456             this.beforeSelectFile(e);
34457             return;
34458         }
34459         
34460         var touches = e.browserEvent.touches;
34461         
34462         if(!touches){
34463             return;
34464         }
34465         
34466         if(touches.length == 1){
34467             this.onMouseDown(e);
34468             return;
34469         }
34470         
34471         if(touches.length != 2){
34472             return;
34473         }
34474         
34475         var coords = [];
34476         
34477         for(var i = 0, finger; finger = touches[i]; i++){
34478             coords.push(finger.pageX, finger.pageY);
34479         }
34480         
34481         var x = Math.pow(coords[0] - coords[2], 2);
34482         var y = Math.pow(coords[1] - coords[3], 2);
34483         
34484         this.startDistance = Math.sqrt(x + y);
34485         
34486         this.startScale = this.scale;
34487         
34488         this.pinching = true;
34489         this.dragable = false;
34490         
34491     },
34492     
34493     onTouchMove : function(e)
34494     {
34495         if(!this.pinching && !this.dragable){
34496             return;
34497         }
34498         
34499         var touches = e.browserEvent.touches;
34500         
34501         if(!touches){
34502             return;
34503         }
34504         
34505         if(this.dragable){
34506             this.onMouseMove(e);
34507             return;
34508         }
34509         
34510         var coords = [];
34511         
34512         for(var i = 0, finger; finger = touches[i]; i++){
34513             coords.push(finger.pageX, finger.pageY);
34514         }
34515         
34516         var x = Math.pow(coords[0] - coords[2], 2);
34517         var y = Math.pow(coords[1] - coords[3], 2);
34518         
34519         this.endDistance = Math.sqrt(x + y);
34520         
34521         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
34522         
34523         if(!this.zoomable()){
34524             this.scale = this.startScale;
34525             return;
34526         }
34527         
34528         this.draw();
34529         
34530     },
34531     
34532     onTouchEnd : function(e)
34533     {
34534         this.pinching = false;
34535         this.dragable = false;
34536         
34537     },
34538     
34539     process : function(file, crop)
34540     {
34541         if(this.loadMask){
34542             this.maskEl.mask(this.loadingText);
34543         }
34544         
34545         this.xhr = new XMLHttpRequest();
34546         
34547         file.xhr = this.xhr;
34548
34549         this.xhr.open(this.method, this.url, true);
34550         
34551         var headers = {
34552             "Accept": "application/json",
34553             "Cache-Control": "no-cache",
34554             "X-Requested-With": "XMLHttpRequest"
34555         };
34556         
34557         for (var headerName in headers) {
34558             var headerValue = headers[headerName];
34559             if (headerValue) {
34560                 this.xhr.setRequestHeader(headerName, headerValue);
34561             }
34562         }
34563         
34564         var _this = this;
34565         
34566         this.xhr.onload = function()
34567         {
34568             _this.xhrOnLoad(_this.xhr);
34569         }
34570         
34571         this.xhr.onerror = function()
34572         {
34573             _this.xhrOnError(_this.xhr);
34574         }
34575         
34576         var formData = new FormData();
34577
34578         formData.append('returnHTML', 'NO');
34579         
34580         if(crop){
34581             formData.append('crop', crop);
34582         }
34583         
34584         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
34585             formData.append(this.paramName, file, file.name);
34586         }
34587         
34588         if(typeof(file.filename) != 'undefined'){
34589             formData.append('filename', file.filename);
34590         }
34591         
34592         if(typeof(file.mimetype) != 'undefined'){
34593             formData.append('mimetype', file.mimetype);
34594         }
34595         
34596         if(this.fireEvent('arrange', this, formData) != false){
34597             this.xhr.send(formData);
34598         };
34599     },
34600     
34601     xhrOnLoad : function(xhr)
34602     {
34603         if(this.loadMask){
34604             this.maskEl.unmask();
34605         }
34606         
34607         if (xhr.readyState !== 4) {
34608             this.fireEvent('exception', this, xhr);
34609             return;
34610         }
34611
34612         var response = Roo.decode(xhr.responseText);
34613         
34614         if(!response.success){
34615             this.fireEvent('exception', this, xhr);
34616             return;
34617         }
34618         
34619         var response = Roo.decode(xhr.responseText);
34620         
34621         this.fireEvent('upload', this, response);
34622         
34623     },
34624     
34625     xhrOnError : function()
34626     {
34627         if(this.loadMask){
34628             this.maskEl.unmask();
34629         }
34630         
34631         Roo.log('xhr on error');
34632         
34633         var response = Roo.decode(xhr.responseText);
34634           
34635         Roo.log(response);
34636         
34637     },
34638     
34639     prepare : function(file)
34640     {   
34641         if(this.loadMask){
34642             this.maskEl.mask(this.loadingText);
34643         }
34644         
34645         this.file = false;
34646         this.exif = {};
34647         
34648         if(typeof(file) === 'string'){
34649             this.loadCanvas(file);
34650             return;
34651         }
34652         
34653         if(!file || !this.urlAPI){
34654             return;
34655         }
34656         
34657         this.file = file;
34658         this.cropType = file.type;
34659         
34660         var _this = this;
34661         
34662         if(this.fireEvent('prepare', this, this.file) != false){
34663             
34664             var reader = new FileReader();
34665             
34666             reader.onload = function (e) {
34667                 if (e.target.error) {
34668                     Roo.log(e.target.error);
34669                     return;
34670                 }
34671                 
34672                 var buffer = e.target.result,
34673                     dataView = new DataView(buffer),
34674                     offset = 2,
34675                     maxOffset = dataView.byteLength - 4,
34676                     markerBytes,
34677                     markerLength;
34678                 
34679                 if (dataView.getUint16(0) === 0xffd8) {
34680                     while (offset < maxOffset) {
34681                         markerBytes = dataView.getUint16(offset);
34682                         
34683                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
34684                             markerLength = dataView.getUint16(offset + 2) + 2;
34685                             if (offset + markerLength > dataView.byteLength) {
34686                                 Roo.log('Invalid meta data: Invalid segment size.');
34687                                 break;
34688                             }
34689                             
34690                             if(markerBytes == 0xffe1){
34691                                 _this.parseExifData(
34692                                     dataView,
34693                                     offset,
34694                                     markerLength
34695                                 );
34696                             }
34697                             
34698                             offset += markerLength;
34699                             
34700                             continue;
34701                         }
34702                         
34703                         break;
34704                     }
34705                     
34706                 }
34707                 
34708                 var url = _this.urlAPI.createObjectURL(_this.file);
34709                 
34710                 _this.loadCanvas(url);
34711                 
34712                 return;
34713             }
34714             
34715             reader.readAsArrayBuffer(this.file);
34716             
34717         }
34718         
34719     },
34720     
34721     parseExifData : function(dataView, offset, length)
34722     {
34723         var tiffOffset = offset + 10,
34724             littleEndian,
34725             dirOffset;
34726     
34727         if (dataView.getUint32(offset + 4) !== 0x45786966) {
34728             // No Exif data, might be XMP data instead
34729             return;
34730         }
34731         
34732         // Check for the ASCII code for "Exif" (0x45786966):
34733         if (dataView.getUint32(offset + 4) !== 0x45786966) {
34734             // No Exif data, might be XMP data instead
34735             return;
34736         }
34737         if (tiffOffset + 8 > dataView.byteLength) {
34738             Roo.log('Invalid Exif data: Invalid segment size.');
34739             return;
34740         }
34741         // Check for the two null bytes:
34742         if (dataView.getUint16(offset + 8) !== 0x0000) {
34743             Roo.log('Invalid Exif data: Missing byte alignment offset.');
34744             return;
34745         }
34746         // Check the byte alignment:
34747         switch (dataView.getUint16(tiffOffset)) {
34748         case 0x4949:
34749             littleEndian = true;
34750             break;
34751         case 0x4D4D:
34752             littleEndian = false;
34753             break;
34754         default:
34755             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
34756             return;
34757         }
34758         // Check for the TIFF tag marker (0x002A):
34759         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
34760             Roo.log('Invalid Exif data: Missing TIFF marker.');
34761             return;
34762         }
34763         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
34764         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
34765         
34766         this.parseExifTags(
34767             dataView,
34768             tiffOffset,
34769             tiffOffset + dirOffset,
34770             littleEndian
34771         );
34772     },
34773     
34774     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
34775     {
34776         var tagsNumber,
34777             dirEndOffset,
34778             i;
34779         if (dirOffset + 6 > dataView.byteLength) {
34780             Roo.log('Invalid Exif data: Invalid directory offset.');
34781             return;
34782         }
34783         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
34784         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
34785         if (dirEndOffset + 4 > dataView.byteLength) {
34786             Roo.log('Invalid Exif data: Invalid directory size.');
34787             return;
34788         }
34789         for (i = 0; i < tagsNumber; i += 1) {
34790             this.parseExifTag(
34791                 dataView,
34792                 tiffOffset,
34793                 dirOffset + 2 + 12 * i, // tag offset
34794                 littleEndian
34795             );
34796         }
34797         // Return the offset to the next directory:
34798         return dataView.getUint32(dirEndOffset, littleEndian);
34799     },
34800     
34801     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
34802     {
34803         var tag = dataView.getUint16(offset, littleEndian);
34804         
34805         this.exif[tag] = this.getExifValue(
34806             dataView,
34807             tiffOffset,
34808             offset,
34809             dataView.getUint16(offset + 2, littleEndian), // tag type
34810             dataView.getUint32(offset + 4, littleEndian), // tag length
34811             littleEndian
34812         );
34813     },
34814     
34815     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
34816     {
34817         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
34818             tagSize,
34819             dataOffset,
34820             values,
34821             i,
34822             str,
34823             c;
34824     
34825         if (!tagType) {
34826             Roo.log('Invalid Exif data: Invalid tag type.');
34827             return;
34828         }
34829         
34830         tagSize = tagType.size * length;
34831         // Determine if the value is contained in the dataOffset bytes,
34832         // or if the value at the dataOffset is a pointer to the actual data:
34833         dataOffset = tagSize > 4 ?
34834                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
34835         if (dataOffset + tagSize > dataView.byteLength) {
34836             Roo.log('Invalid Exif data: Invalid data offset.');
34837             return;
34838         }
34839         if (length === 1) {
34840             return tagType.getValue(dataView, dataOffset, littleEndian);
34841         }
34842         values = [];
34843         for (i = 0; i < length; i += 1) {
34844             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
34845         }
34846         
34847         if (tagType.ascii) {
34848             str = '';
34849             // Concatenate the chars:
34850             for (i = 0; i < values.length; i += 1) {
34851                 c = values[i];
34852                 // Ignore the terminating NULL byte(s):
34853                 if (c === '\u0000') {
34854                     break;
34855                 }
34856                 str += c;
34857             }
34858             return str;
34859         }
34860         return values;
34861     }
34862     
34863 });
34864
34865 Roo.apply(Roo.bootstrap.UploadCropbox, {
34866     tags : {
34867         'Orientation': 0x0112
34868     },
34869     
34870     Orientation: {
34871             1: 0, //'top-left',
34872 //            2: 'top-right',
34873             3: 180, //'bottom-right',
34874 //            4: 'bottom-left',
34875 //            5: 'left-top',
34876             6: 90, //'right-top',
34877 //            7: 'right-bottom',
34878             8: 270 //'left-bottom'
34879     },
34880     
34881     exifTagTypes : {
34882         // byte, 8-bit unsigned int:
34883         1: {
34884             getValue: function (dataView, dataOffset) {
34885                 return dataView.getUint8(dataOffset);
34886             },
34887             size: 1
34888         },
34889         // ascii, 8-bit byte:
34890         2: {
34891             getValue: function (dataView, dataOffset) {
34892                 return String.fromCharCode(dataView.getUint8(dataOffset));
34893             },
34894             size: 1,
34895             ascii: true
34896         },
34897         // short, 16 bit int:
34898         3: {
34899             getValue: function (dataView, dataOffset, littleEndian) {
34900                 return dataView.getUint16(dataOffset, littleEndian);
34901             },
34902             size: 2
34903         },
34904         // long, 32 bit int:
34905         4: {
34906             getValue: function (dataView, dataOffset, littleEndian) {
34907                 return dataView.getUint32(dataOffset, littleEndian);
34908             },
34909             size: 4
34910         },
34911         // rational = two long values, first is numerator, second is denominator:
34912         5: {
34913             getValue: function (dataView, dataOffset, littleEndian) {
34914                 return dataView.getUint32(dataOffset, littleEndian) /
34915                     dataView.getUint32(dataOffset + 4, littleEndian);
34916             },
34917             size: 8
34918         },
34919         // slong, 32 bit signed int:
34920         9: {
34921             getValue: function (dataView, dataOffset, littleEndian) {
34922                 return dataView.getInt32(dataOffset, littleEndian);
34923             },
34924             size: 4
34925         },
34926         // srational, two slongs, first is numerator, second is denominator:
34927         10: {
34928             getValue: function (dataView, dataOffset, littleEndian) {
34929                 return dataView.getInt32(dataOffset, littleEndian) /
34930                     dataView.getInt32(dataOffset + 4, littleEndian);
34931             },
34932             size: 8
34933         }
34934     },
34935     
34936     footer : {
34937         STANDARD : [
34938             {
34939                 tag : 'div',
34940                 cls : 'btn-group roo-upload-cropbox-rotate-left',
34941                 action : 'rotate-left',
34942                 cn : [
34943                     {
34944                         tag : 'button',
34945                         cls : 'btn btn-default',
34946                         html : '<i class="fa fa-undo"></i>'
34947                     }
34948                 ]
34949             },
34950             {
34951                 tag : 'div',
34952                 cls : 'btn-group roo-upload-cropbox-picture',
34953                 action : 'picture',
34954                 cn : [
34955                     {
34956                         tag : 'button',
34957                         cls : 'btn btn-default',
34958                         html : '<i class="fa fa-picture-o"></i>'
34959                     }
34960                 ]
34961             },
34962             {
34963                 tag : 'div',
34964                 cls : 'btn-group roo-upload-cropbox-rotate-right',
34965                 action : 'rotate-right',
34966                 cn : [
34967                     {
34968                         tag : 'button',
34969                         cls : 'btn btn-default',
34970                         html : '<i class="fa fa-repeat"></i>'
34971                     }
34972                 ]
34973             }
34974         ],
34975         DOCUMENT : [
34976             {
34977                 tag : 'div',
34978                 cls : 'btn-group roo-upload-cropbox-rotate-left',
34979                 action : 'rotate-left',
34980                 cn : [
34981                     {
34982                         tag : 'button',
34983                         cls : 'btn btn-default',
34984                         html : '<i class="fa fa-undo"></i>'
34985                     }
34986                 ]
34987             },
34988             {
34989                 tag : 'div',
34990                 cls : 'btn-group roo-upload-cropbox-download',
34991                 action : 'download',
34992                 cn : [
34993                     {
34994                         tag : 'button',
34995                         cls : 'btn btn-default',
34996                         html : '<i class="fa fa-download"></i>'
34997                     }
34998                 ]
34999             },
35000             {
35001                 tag : 'div',
35002                 cls : 'btn-group roo-upload-cropbox-crop',
35003                 action : 'crop',
35004                 cn : [
35005                     {
35006                         tag : 'button',
35007                         cls : 'btn btn-default',
35008                         html : '<i class="fa fa-crop"></i>'
35009                     }
35010                 ]
35011             },
35012             {
35013                 tag : 'div',
35014                 cls : 'btn-group roo-upload-cropbox-trash',
35015                 action : 'trash',
35016                 cn : [
35017                     {
35018                         tag : 'button',
35019                         cls : 'btn btn-default',
35020                         html : '<i class="fa fa-trash"></i>'
35021                     }
35022                 ]
35023             },
35024             {
35025                 tag : 'div',
35026                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35027                 action : 'rotate-right',
35028                 cn : [
35029                     {
35030                         tag : 'button',
35031                         cls : 'btn btn-default',
35032                         html : '<i class="fa fa-repeat"></i>'
35033                     }
35034                 ]
35035             }
35036         ],
35037         ROTATOR : [
35038             {
35039                 tag : 'div',
35040                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35041                 action : 'rotate-left',
35042                 cn : [
35043                     {
35044                         tag : 'button',
35045                         cls : 'btn btn-default',
35046                         html : '<i class="fa fa-undo"></i>'
35047                     }
35048                 ]
35049             },
35050             {
35051                 tag : 'div',
35052                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35053                 action : 'rotate-right',
35054                 cn : [
35055                     {
35056                         tag : 'button',
35057                         cls : 'btn btn-default',
35058                         html : '<i class="fa fa-repeat"></i>'
35059                     }
35060                 ]
35061             }
35062         ]
35063     }
35064 });
35065
35066 /*
35067 * Licence: LGPL
35068 */
35069
35070 /**
35071  * @class Roo.bootstrap.DocumentManager
35072  * @extends Roo.bootstrap.Component
35073  * Bootstrap DocumentManager class
35074  * @cfg {String} paramName default 'imageUpload'
35075  * @cfg {String} toolTipName default 'filename'
35076  * @cfg {String} method default POST
35077  * @cfg {String} url action url
35078  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
35079  * @cfg {Boolean} multiple multiple upload default true
35080  * @cfg {Number} thumbSize default 300
35081  * @cfg {String} fieldLabel
35082  * @cfg {Number} labelWidth default 4
35083  * @cfg {String} labelAlign (left|top) default left
35084  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
35085 * @cfg {Number} labellg set the width of label (1-12)
35086  * @cfg {Number} labelmd set the width of label (1-12)
35087  * @cfg {Number} labelsm set the width of label (1-12)
35088  * @cfg {Number} labelxs set the width of label (1-12)
35089  * 
35090  * @constructor
35091  * Create a new DocumentManager
35092  * @param {Object} config The config object
35093  */
35094
35095 Roo.bootstrap.DocumentManager = function(config){
35096     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
35097     
35098     this.files = [];
35099     this.delegates = [];
35100     
35101     this.addEvents({
35102         /**
35103          * @event initial
35104          * Fire when initial the DocumentManager
35105          * @param {Roo.bootstrap.DocumentManager} this
35106          */
35107         "initial" : true,
35108         /**
35109          * @event inspect
35110          * inspect selected file
35111          * @param {Roo.bootstrap.DocumentManager} this
35112          * @param {File} file
35113          */
35114         "inspect" : true,
35115         /**
35116          * @event exception
35117          * Fire when xhr load exception
35118          * @param {Roo.bootstrap.DocumentManager} this
35119          * @param {XMLHttpRequest} xhr
35120          */
35121         "exception" : true,
35122         /**
35123          * @event afterupload
35124          * Fire when xhr load exception
35125          * @param {Roo.bootstrap.DocumentManager} this
35126          * @param {XMLHttpRequest} xhr
35127          */
35128         "afterupload" : true,
35129         /**
35130          * @event prepare
35131          * prepare the form data
35132          * @param {Roo.bootstrap.DocumentManager} this
35133          * @param {Object} formData
35134          */
35135         "prepare" : true,
35136         /**
35137          * @event remove
35138          * Fire when remove the file
35139          * @param {Roo.bootstrap.DocumentManager} this
35140          * @param {Object} file
35141          */
35142         "remove" : true,
35143         /**
35144          * @event refresh
35145          * Fire after refresh the file
35146          * @param {Roo.bootstrap.DocumentManager} this
35147          */
35148         "refresh" : true,
35149         /**
35150          * @event click
35151          * Fire after click the image
35152          * @param {Roo.bootstrap.DocumentManager} this
35153          * @param {Object} file
35154          */
35155         "click" : true,
35156         /**
35157          * @event edit
35158          * Fire when upload a image and editable set to true
35159          * @param {Roo.bootstrap.DocumentManager} this
35160          * @param {Object} file
35161          */
35162         "edit" : true,
35163         /**
35164          * @event beforeselectfile
35165          * Fire before select file
35166          * @param {Roo.bootstrap.DocumentManager} this
35167          */
35168         "beforeselectfile" : true,
35169         /**
35170          * @event process
35171          * Fire before process file
35172          * @param {Roo.bootstrap.DocumentManager} this
35173          * @param {Object} file
35174          */
35175         "process" : true,
35176         /**
35177          * @event previewrendered
35178          * Fire when preview rendered
35179          * @param {Roo.bootstrap.DocumentManager} this
35180          * @param {Object} file
35181          */
35182         "previewrendered" : true,
35183         /**
35184          */
35185         "previewResize" : true
35186         
35187     });
35188 };
35189
35190 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
35191     
35192     boxes : 0,
35193     inputName : '',
35194     thumbSize : 300,
35195     multiple : true,
35196     files : false,
35197     method : 'POST',
35198     url : '',
35199     paramName : 'imageUpload',
35200     toolTipName : 'filename',
35201     fieldLabel : '',
35202     labelWidth : 4,
35203     labelAlign : 'left',
35204     editable : true,
35205     delegates : false,
35206     xhr : false, 
35207     
35208     labellg : 0,
35209     labelmd : 0,
35210     labelsm : 0,
35211     labelxs : 0,
35212     
35213     getAutoCreate : function()
35214     {   
35215         var managerWidget = {
35216             tag : 'div',
35217             cls : 'roo-document-manager',
35218             cn : [
35219                 {
35220                     tag : 'input',
35221                     cls : 'roo-document-manager-selector',
35222                     type : 'file'
35223                 },
35224                 {
35225                     tag : 'div',
35226                     cls : 'roo-document-manager-uploader',
35227                     cn : [
35228                         {
35229                             tag : 'div',
35230                             cls : 'roo-document-manager-upload-btn',
35231                             html : '<i class="fa fa-plus"></i>'
35232                         }
35233                     ]
35234                     
35235                 }
35236             ]
35237         };
35238         
35239         var content = [
35240             {
35241                 tag : 'div',
35242                 cls : 'column col-md-12',
35243                 cn : managerWidget
35244             }
35245         ];
35246         
35247         if(this.fieldLabel.length){
35248             
35249             content = [
35250                 {
35251                     tag : 'div',
35252                     cls : 'column col-md-12',
35253                     html : this.fieldLabel
35254                 },
35255                 {
35256                     tag : 'div',
35257                     cls : 'column col-md-12',
35258                     cn : managerWidget
35259                 }
35260             ];
35261
35262             if(this.labelAlign == 'left'){
35263                 content = [
35264                     {
35265                         tag : 'div',
35266                         cls : 'column',
35267                         html : this.fieldLabel
35268                     },
35269                     {
35270                         tag : 'div',
35271                         cls : 'column',
35272                         cn : managerWidget
35273                     }
35274                 ];
35275                 
35276                 if(this.labelWidth > 12){
35277                     content[0].style = "width: " + this.labelWidth + 'px';
35278                 }
35279
35280                 if(this.labelWidth < 13 && this.labelmd == 0){
35281                     this.labelmd = this.labelWidth;
35282                 }
35283
35284                 if(this.labellg > 0){
35285                     content[0].cls += ' col-lg-' + this.labellg;
35286                     content[1].cls += ' col-lg-' + (12 - this.labellg);
35287                 }
35288
35289                 if(this.labelmd > 0){
35290                     content[0].cls += ' col-md-' + this.labelmd;
35291                     content[1].cls += ' col-md-' + (12 - this.labelmd);
35292                 }
35293
35294                 if(this.labelsm > 0){
35295                     content[0].cls += ' col-sm-' + this.labelsm;
35296                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
35297                 }
35298
35299                 if(this.labelxs > 0){
35300                     content[0].cls += ' col-xs-' + this.labelxs;
35301                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
35302                 }
35303                 
35304             }
35305         }
35306         
35307         var cfg = {
35308             tag : 'div',
35309             cls : 'row clearfix',
35310             cn : content
35311         };
35312         
35313         return cfg;
35314         
35315     },
35316     
35317     initEvents : function()
35318     {
35319         this.managerEl = this.el.select('.roo-document-manager', true).first();
35320         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35321         
35322         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
35323         this.selectorEl.hide();
35324         
35325         if(this.multiple){
35326             this.selectorEl.attr('multiple', 'multiple');
35327         }
35328         
35329         this.selectorEl.on('change', this.onFileSelected, this);
35330         
35331         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
35332         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35333         
35334         this.uploader.on('click', this.onUploaderClick, this);
35335         
35336         this.renderProgressDialog();
35337         
35338         var _this = this;
35339         
35340         window.addEventListener("resize", function() { _this.refresh(); } );
35341         
35342         this.fireEvent('initial', this);
35343     },
35344     
35345     renderProgressDialog : function()
35346     {
35347         var _this = this;
35348         
35349         this.progressDialog = new Roo.bootstrap.Modal({
35350             cls : 'roo-document-manager-progress-dialog',
35351             allow_close : false,
35352             animate : false,
35353             title : '',
35354             buttons : [
35355                 {
35356                     name  :'cancel',
35357                     weight : 'danger',
35358                     html : 'Cancel'
35359                 }
35360             ], 
35361             listeners : { 
35362                 btnclick : function() {
35363                     _this.uploadCancel();
35364                     this.hide();
35365                 }
35366             }
35367         });
35368          
35369         this.progressDialog.render(Roo.get(document.body));
35370          
35371         this.progress = new Roo.bootstrap.Progress({
35372             cls : 'roo-document-manager-progress',
35373             active : true,
35374             striped : true
35375         });
35376         
35377         this.progress.render(this.progressDialog.getChildContainer());
35378         
35379         this.progressBar = new Roo.bootstrap.ProgressBar({
35380             cls : 'roo-document-manager-progress-bar',
35381             aria_valuenow : 0,
35382             aria_valuemin : 0,
35383             aria_valuemax : 12,
35384             panel : 'success'
35385         });
35386         
35387         this.progressBar.render(this.progress.getChildContainer());
35388     },
35389     
35390     onUploaderClick : function(e)
35391     {
35392         e.preventDefault();
35393      
35394         if(this.fireEvent('beforeselectfile', this) != false){
35395             this.selectorEl.dom.click();
35396         }
35397         
35398     },
35399     
35400     onFileSelected : function(e)
35401     {
35402         e.preventDefault();
35403         
35404         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35405             return;
35406         }
35407         
35408         Roo.each(this.selectorEl.dom.files, function(file){
35409             if(this.fireEvent('inspect', this, file) != false){
35410                 this.files.push(file);
35411             }
35412         }, this);
35413         
35414         this.queue();
35415         
35416     },
35417     
35418     queue : function()
35419     {
35420         this.selectorEl.dom.value = '';
35421         
35422         if(!this.files || !this.files.length){
35423             return;
35424         }
35425         
35426         if(this.boxes > 0 && this.files.length > this.boxes){
35427             this.files = this.files.slice(0, this.boxes);
35428         }
35429         
35430         this.uploader.show();
35431         
35432         if(this.boxes > 0 && this.files.length > this.boxes - 1){
35433             this.uploader.hide();
35434         }
35435         
35436         var _this = this;
35437         
35438         var files = [];
35439         
35440         var docs = [];
35441         
35442         Roo.each(this.files, function(file){
35443             
35444             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35445                 var f = this.renderPreview(file);
35446                 files.push(f);
35447                 return;
35448             }
35449             
35450             if(file.type.indexOf('image') != -1){
35451                 this.delegates.push(
35452                     (function(){
35453                         _this.process(file);
35454                     }).createDelegate(this)
35455                 );
35456         
35457                 return;
35458             }
35459             
35460             docs.push(
35461                 (function(){
35462                     _this.process(file);
35463                 }).createDelegate(this)
35464             );
35465             
35466         }, this);
35467         
35468         this.files = files;
35469         
35470         this.delegates = this.delegates.concat(docs);
35471         
35472         if(!this.delegates.length){
35473             this.refresh();
35474             return;
35475         }
35476         
35477         this.progressBar.aria_valuemax = this.delegates.length;
35478         
35479         this.arrange();
35480         
35481         return;
35482     },
35483     
35484     arrange : function()
35485     {
35486         if(!this.delegates.length){
35487             this.progressDialog.hide();
35488             this.refresh();
35489             return;
35490         }
35491         
35492         var delegate = this.delegates.shift();
35493         
35494         this.progressDialog.show();
35495         
35496         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
35497         
35498         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
35499         
35500         delegate();
35501     },
35502     
35503     refresh : function()
35504     {
35505         this.uploader.show();
35506         
35507         if(this.boxes > 0 && this.files.length > this.boxes - 1){
35508             this.uploader.hide();
35509         }
35510         
35511         Roo.isTouch ? this.closable(false) : this.closable(true);
35512         
35513         this.fireEvent('refresh', this);
35514     },
35515     
35516     onRemove : function(e, el, o)
35517     {
35518         e.preventDefault();
35519         
35520         this.fireEvent('remove', this, o);
35521         
35522     },
35523     
35524     remove : function(o)
35525     {
35526         var files = [];
35527         
35528         Roo.each(this.files, function(file){
35529             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
35530                 files.push(file);
35531                 return;
35532             }
35533
35534             o.target.remove();
35535
35536         }, this);
35537         
35538         this.files = files;
35539         
35540         this.refresh();
35541     },
35542     
35543     clear : function()
35544     {
35545         Roo.each(this.files, function(file){
35546             if(!file.target){
35547                 return;
35548             }
35549             
35550             file.target.remove();
35551
35552         }, this);
35553         
35554         this.files = [];
35555         
35556         this.refresh();
35557     },
35558     
35559     onClick : function(e, el, o)
35560     {
35561         e.preventDefault();
35562         
35563         this.fireEvent('click', this, o);
35564         
35565     },
35566     
35567     closable : function(closable)
35568     {
35569         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
35570             
35571             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35572             
35573             if(closable){
35574                 el.show();
35575                 return;
35576             }
35577             
35578             el.hide();
35579             
35580         }, this);
35581     },
35582     
35583     xhrOnLoad : function(xhr)
35584     {
35585         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35586             el.remove();
35587         }, this);
35588         
35589         if (xhr.readyState !== 4) {
35590             this.arrange();
35591             this.fireEvent('exception', this, xhr);
35592             return;
35593         }
35594
35595         var response = Roo.decode(xhr.responseText);
35596         
35597         if(!response.success){
35598             this.arrange();
35599             this.fireEvent('exception', this, xhr);
35600             return;
35601         }
35602         
35603         var file = this.renderPreview(response.data);
35604         
35605         this.files.push(file);
35606         
35607         this.arrange();
35608         
35609         this.fireEvent('afterupload', this, xhr);
35610         
35611     },
35612     
35613     xhrOnError : function(xhr)
35614     {
35615         Roo.log('xhr on error');
35616         
35617         var response = Roo.decode(xhr.responseText);
35618           
35619         Roo.log(response);
35620         
35621         this.arrange();
35622     },
35623     
35624     process : function(file)
35625     {
35626         if(this.fireEvent('process', this, file) !== false){
35627             if(this.editable && file.type.indexOf('image') != -1){
35628                 this.fireEvent('edit', this, file);
35629                 return;
35630             }
35631
35632             this.uploadStart(file, false);
35633
35634             return;
35635         }
35636         
35637     },
35638     
35639     uploadStart : function(file, crop)
35640     {
35641         this.xhr = new XMLHttpRequest();
35642         
35643         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35644             this.arrange();
35645             return;
35646         }
35647         
35648         file.xhr = this.xhr;
35649             
35650         this.managerEl.createChild({
35651             tag : 'div',
35652             cls : 'roo-document-manager-loading',
35653             cn : [
35654                 {
35655                     tag : 'div',
35656                     tooltip : file.name,
35657                     cls : 'roo-document-manager-thumb',
35658                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
35659                 }
35660             ]
35661
35662         });
35663
35664         this.xhr.open(this.method, this.url, true);
35665         
35666         var headers = {
35667             "Accept": "application/json",
35668             "Cache-Control": "no-cache",
35669             "X-Requested-With": "XMLHttpRequest"
35670         };
35671         
35672         for (var headerName in headers) {
35673             var headerValue = headers[headerName];
35674             if (headerValue) {
35675                 this.xhr.setRequestHeader(headerName, headerValue);
35676             }
35677         }
35678         
35679         var _this = this;
35680         
35681         this.xhr.onload = function()
35682         {
35683             _this.xhrOnLoad(_this.xhr);
35684         }
35685         
35686         this.xhr.onerror = function()
35687         {
35688             _this.xhrOnError(_this.xhr);
35689         }
35690         
35691         var formData = new FormData();
35692
35693         formData.append('returnHTML', 'NO');
35694         
35695         if(crop){
35696             formData.append('crop', crop);
35697         }
35698         
35699         formData.append(this.paramName, file, file.name);
35700         
35701         var options = {
35702             file : file, 
35703             manually : false
35704         };
35705         
35706         if(this.fireEvent('prepare', this, formData, options) != false){
35707             
35708             if(options.manually){
35709                 return;
35710             }
35711             
35712             this.xhr.send(formData);
35713             return;
35714         };
35715         
35716         this.uploadCancel();
35717     },
35718     
35719     uploadCancel : function()
35720     {
35721         if (this.xhr) {
35722             this.xhr.abort();
35723         }
35724         
35725         this.delegates = [];
35726         
35727         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35728             el.remove();
35729         }, this);
35730         
35731         this.arrange();
35732     },
35733     
35734     renderPreview : function(file)
35735     {
35736         if(typeof(file.target) != 'undefined' && file.target){
35737             return file;
35738         }
35739         
35740         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
35741         
35742         var previewEl = this.managerEl.createChild({
35743             tag : 'div',
35744             cls : 'roo-document-manager-preview',
35745             cn : [
35746                 {
35747                     tag : 'div',
35748                     tooltip : file[this.toolTipName],
35749                     cls : 'roo-document-manager-thumb',
35750                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
35751                 },
35752                 {
35753                     tag : 'button',
35754                     cls : 'close',
35755                     html : '<i class="fa fa-times-circle"></i>'
35756                 }
35757             ]
35758         });
35759
35760         var close = previewEl.select('button.close', true).first();
35761
35762         close.on('click', this.onRemove, this, file);
35763
35764         file.target = previewEl;
35765
35766         var image = previewEl.select('img', true).first();
35767         
35768         var _this = this;
35769         
35770         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
35771         
35772         image.on('click', this.onClick, this, file);
35773         
35774         this.fireEvent('previewrendered', this, file);
35775         
35776         return file;
35777         
35778     },
35779     
35780     onPreviewLoad : function(file, image)
35781     {
35782         if(typeof(file.target) == 'undefined' || !file.target){
35783             return;
35784         }
35785         
35786         var width = image.dom.naturalWidth || image.dom.width;
35787         var height = image.dom.naturalHeight || image.dom.height;
35788         
35789         if(!this.previewResize) {
35790             return;
35791         }
35792         
35793         if(width > height){
35794             file.target.addClass('wide');
35795             return;
35796         }
35797         
35798         file.target.addClass('tall');
35799         return;
35800         
35801     },
35802     
35803     uploadFromSource : function(file, crop)
35804     {
35805         this.xhr = new XMLHttpRequest();
35806         
35807         this.managerEl.createChild({
35808             tag : 'div',
35809             cls : 'roo-document-manager-loading',
35810             cn : [
35811                 {
35812                     tag : 'div',
35813                     tooltip : file.name,
35814                     cls : 'roo-document-manager-thumb',
35815                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
35816                 }
35817             ]
35818
35819         });
35820
35821         this.xhr.open(this.method, this.url, true);
35822         
35823         var headers = {
35824             "Accept": "application/json",
35825             "Cache-Control": "no-cache",
35826             "X-Requested-With": "XMLHttpRequest"
35827         };
35828         
35829         for (var headerName in headers) {
35830             var headerValue = headers[headerName];
35831             if (headerValue) {
35832                 this.xhr.setRequestHeader(headerName, headerValue);
35833             }
35834         }
35835         
35836         var _this = this;
35837         
35838         this.xhr.onload = function()
35839         {
35840             _this.xhrOnLoad(_this.xhr);
35841         }
35842         
35843         this.xhr.onerror = function()
35844         {
35845             _this.xhrOnError(_this.xhr);
35846         }
35847         
35848         var formData = new FormData();
35849
35850         formData.append('returnHTML', 'NO');
35851         
35852         formData.append('crop', crop);
35853         
35854         if(typeof(file.filename) != 'undefined'){
35855             formData.append('filename', file.filename);
35856         }
35857         
35858         if(typeof(file.mimetype) != 'undefined'){
35859             formData.append('mimetype', file.mimetype);
35860         }
35861         
35862         Roo.log(formData);
35863         
35864         if(this.fireEvent('prepare', this, formData) != false){
35865             this.xhr.send(formData);
35866         };
35867     }
35868 });
35869
35870 /*
35871 * Licence: LGPL
35872 */
35873
35874 /**
35875  * @class Roo.bootstrap.DocumentViewer
35876  * @extends Roo.bootstrap.Component
35877  * Bootstrap DocumentViewer class
35878  * @cfg {Boolean} showDownload (true|false) show download button (default true)
35879  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
35880  * 
35881  * @constructor
35882  * Create a new DocumentViewer
35883  * @param {Object} config The config object
35884  */
35885
35886 Roo.bootstrap.DocumentViewer = function(config){
35887     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
35888     
35889     this.addEvents({
35890         /**
35891          * @event initial
35892          * Fire after initEvent
35893          * @param {Roo.bootstrap.DocumentViewer} this
35894          */
35895         "initial" : true,
35896         /**
35897          * @event click
35898          * Fire after click
35899          * @param {Roo.bootstrap.DocumentViewer} this
35900          */
35901         "click" : true,
35902         /**
35903          * @event download
35904          * Fire after download button
35905          * @param {Roo.bootstrap.DocumentViewer} this
35906          */
35907         "download" : true,
35908         /**
35909          * @event trash
35910          * Fire after trash button
35911          * @param {Roo.bootstrap.DocumentViewer} this
35912          */
35913         "trash" : true
35914         
35915     });
35916 };
35917
35918 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
35919     
35920     showDownload : true,
35921     
35922     showTrash : true,
35923     
35924     getAutoCreate : function()
35925     {
35926         var cfg = {
35927             tag : 'div',
35928             cls : 'roo-document-viewer',
35929             cn : [
35930                 {
35931                     tag : 'div',
35932                     cls : 'roo-document-viewer-body',
35933                     cn : [
35934                         {
35935                             tag : 'div',
35936                             cls : 'roo-document-viewer-thumb',
35937                             cn : [
35938                                 {
35939                                     tag : 'img',
35940                                     cls : 'roo-document-viewer-image'
35941                                 }
35942                             ]
35943                         }
35944                     ]
35945                 },
35946                 {
35947                     tag : 'div',
35948                     cls : 'roo-document-viewer-footer',
35949                     cn : {
35950                         tag : 'div',
35951                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
35952                         cn : [
35953                             {
35954                                 tag : 'div',
35955                                 cls : 'btn-group roo-document-viewer-download',
35956                                 cn : [
35957                                     {
35958                                         tag : 'button',
35959                                         cls : 'btn btn-default',
35960                                         html : '<i class="fa fa-download"></i>'
35961                                     }
35962                                 ]
35963                             },
35964                             {
35965                                 tag : 'div',
35966                                 cls : 'btn-group roo-document-viewer-trash',
35967                                 cn : [
35968                                     {
35969                                         tag : 'button',
35970                                         cls : 'btn btn-default',
35971                                         html : '<i class="fa fa-trash"></i>'
35972                                     }
35973                                 ]
35974                             }
35975                         ]
35976                     }
35977                 }
35978             ]
35979         };
35980         
35981         return cfg;
35982     },
35983     
35984     initEvents : function()
35985     {
35986         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
35987         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35988         
35989         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
35990         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35991         
35992         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
35993         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35994         
35995         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
35996         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
35997         
35998         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
35999         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
36000         
36001         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
36002         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
36003         
36004         this.bodyEl.on('click', this.onClick, this);
36005         this.downloadBtn.on('click', this.onDownload, this);
36006         this.trashBtn.on('click', this.onTrash, this);
36007         
36008         this.downloadBtn.hide();
36009         this.trashBtn.hide();
36010         
36011         if(this.showDownload){
36012             this.downloadBtn.show();
36013         }
36014         
36015         if(this.showTrash){
36016             this.trashBtn.show();
36017         }
36018         
36019         if(!this.showDownload && !this.showTrash) {
36020             this.footerEl.hide();
36021         }
36022         
36023     },
36024     
36025     initial : function()
36026     {
36027         this.fireEvent('initial', this);
36028         
36029     },
36030     
36031     onClick : function(e)
36032     {
36033         e.preventDefault();
36034         
36035         this.fireEvent('click', this);
36036     },
36037     
36038     onDownload : function(e)
36039     {
36040         e.preventDefault();
36041         
36042         this.fireEvent('download', this);
36043     },
36044     
36045     onTrash : function(e)
36046     {
36047         e.preventDefault();
36048         
36049         this.fireEvent('trash', this);
36050     }
36051     
36052 });
36053 /*
36054  * - LGPL
36055  *
36056  * FieldLabel
36057  * 
36058  */
36059
36060 /**
36061  * @class Roo.bootstrap.form.FieldLabel
36062  * @extends Roo.bootstrap.Component
36063  * Bootstrap FieldLabel class
36064  * @cfg {String} html contents of the element
36065  * @cfg {String} tag tag of the element default label
36066  * @cfg {String} cls class of the element
36067  * @cfg {String} target label target 
36068  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
36069  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
36070  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
36071  * @cfg {String} iconTooltip default "This field is required"
36072  * @cfg {String} indicatorpos (left|right) default left
36073  * 
36074  * @constructor
36075  * Create a new FieldLabel
36076  * @param {Object} config The config object
36077  */
36078
36079 Roo.bootstrap.form.FieldLabel = function(config){
36080     Roo.bootstrap.Element.superclass.constructor.call(this, config);
36081     
36082     this.addEvents({
36083             /**
36084              * @event invalid
36085              * Fires after the field has been marked as invalid.
36086              * @param {Roo.form.FieldLabel} this
36087              * @param {String} msg The validation message
36088              */
36089             invalid : true,
36090             /**
36091              * @event valid
36092              * Fires after the field has been validated with no errors.
36093              * @param {Roo.form.FieldLabel} this
36094              */
36095             valid : true
36096         });
36097 };
36098
36099 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
36100     
36101     tag: 'label',
36102     cls: '',
36103     html: '',
36104     target: '',
36105     allowBlank : true,
36106     invalidClass : 'has-warning',
36107     validClass : 'has-success',
36108     iconTooltip : 'This field is required',
36109     indicatorpos : 'left',
36110     
36111     getAutoCreate : function(){
36112         
36113         var cls = "";
36114         if (!this.allowBlank) {
36115             cls  = "visible";
36116         }
36117         
36118         var cfg = {
36119             tag : this.tag,
36120             cls : 'roo-bootstrap-field-label ' + this.cls,
36121             for : this.target,
36122             cn : [
36123                 {
36124                     tag : 'i',
36125                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
36126                     tooltip : this.iconTooltip
36127                 },
36128                 {
36129                     tag : 'span',
36130                     html : this.html
36131                 }
36132             ] 
36133         };
36134         
36135         if(this.indicatorpos == 'right'){
36136             var cfg = {
36137                 tag : this.tag,
36138                 cls : 'roo-bootstrap-field-label ' + this.cls,
36139                 for : this.target,
36140                 cn : [
36141                     {
36142                         tag : 'span',
36143                         html : this.html
36144                     },
36145                     {
36146                         tag : 'i',
36147                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
36148                         tooltip : this.iconTooltip
36149                     }
36150                 ] 
36151             };
36152         }
36153         
36154         return cfg;
36155     },
36156     
36157     initEvents: function() 
36158     {
36159         Roo.bootstrap.Element.superclass.initEvents.call(this);
36160         
36161         this.indicator = this.indicatorEl();
36162         
36163         if(this.indicator){
36164             this.indicator.removeClass('visible');
36165             this.indicator.addClass('invisible');
36166         }
36167         
36168         Roo.bootstrap.form.FieldLabel.register(this);
36169     },
36170     
36171     indicatorEl : function()
36172     {
36173         var indicator = this.el.select('i.roo-required-indicator',true).first();
36174         
36175         if(!indicator){
36176             return false;
36177         }
36178         
36179         return indicator;
36180         
36181     },
36182     
36183     /**
36184      * Mark this field as valid
36185      */
36186     markValid : function()
36187     {
36188         if(this.indicator){
36189             this.indicator.removeClass('visible');
36190             this.indicator.addClass('invisible');
36191         }
36192         if (Roo.bootstrap.version == 3) {
36193             this.el.removeClass(this.invalidClass);
36194             this.el.addClass(this.validClass);
36195         } else {
36196             this.el.removeClass('is-invalid');
36197             this.el.addClass('is-valid');
36198         }
36199         
36200         
36201         this.fireEvent('valid', this);
36202     },
36203     
36204     /**
36205      * Mark this field as invalid
36206      * @param {String} msg The validation message
36207      */
36208     markInvalid : function(msg)
36209     {
36210         if(this.indicator){
36211             this.indicator.removeClass('invisible');
36212             this.indicator.addClass('visible');
36213         }
36214           if (Roo.bootstrap.version == 3) {
36215             this.el.removeClass(this.validClass);
36216             this.el.addClass(this.invalidClass);
36217         } else {
36218             this.el.removeClass('is-valid');
36219             this.el.addClass('is-invalid');
36220         }
36221         
36222         
36223         this.fireEvent('invalid', this, msg);
36224     }
36225     
36226    
36227 });
36228
36229 Roo.apply(Roo.bootstrap.form.FieldLabel, {
36230     
36231     groups: {},
36232     
36233      /**
36234     * register a FieldLabel Group
36235     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
36236     */
36237     register : function(label)
36238     {
36239         if(this.groups.hasOwnProperty(label.target)){
36240             return;
36241         }
36242      
36243         this.groups[label.target] = label;
36244         
36245     },
36246     /**
36247     * fetch a FieldLabel Group based on the target
36248     * @param {string} target
36249     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
36250     */
36251     get: function(target) {
36252         if (typeof(this.groups[target]) == 'undefined') {
36253             return false;
36254         }
36255         
36256         return this.groups[target] ;
36257     }
36258 });
36259
36260  
36261
36262  /*
36263  * - LGPL
36264  *
36265  * page DateSplitField.
36266  * 
36267  */
36268
36269
36270 /**
36271  * @class Roo.bootstrap.form.DateSplitField
36272  * @extends Roo.bootstrap.Component
36273  * Bootstrap DateSplitField class
36274  * @cfg {string} fieldLabel - the label associated
36275  * @cfg {Number} labelWidth set the width of label (0-12)
36276  * @cfg {String} labelAlign (top|left)
36277  * @cfg {Boolean} dayAllowBlank (true|false) default false
36278  * @cfg {Boolean} monthAllowBlank (true|false) default false
36279  * @cfg {Boolean} yearAllowBlank (true|false) default false
36280  * @cfg {string} dayPlaceholder 
36281  * @cfg {string} monthPlaceholder
36282  * @cfg {string} yearPlaceholder
36283  * @cfg {string} dayFormat default 'd'
36284  * @cfg {string} monthFormat default 'm'
36285  * @cfg {string} yearFormat default 'Y'
36286  * @cfg {Number} labellg set the width of label (1-12)
36287  * @cfg {Number} labelmd set the width of label (1-12)
36288  * @cfg {Number} labelsm set the width of label (1-12)
36289  * @cfg {Number} labelxs set the width of label (1-12)
36290
36291  *     
36292  * @constructor
36293  * Create a new DateSplitField
36294  * @param {Object} config The config object
36295  */
36296
36297 Roo.bootstrap.form.DateSplitField = function(config){
36298     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
36299     
36300     this.addEvents({
36301         // raw events
36302          /**
36303          * @event years
36304          * getting the data of years
36305          * @param {Roo.bootstrap.form.DateSplitField} this
36306          * @param {Object} years
36307          */
36308         "years" : true,
36309         /**
36310          * @event days
36311          * getting the data of days
36312          * @param {Roo.bootstrap.form.DateSplitField} this
36313          * @param {Object} days
36314          */
36315         "days" : true,
36316         /**
36317          * @event invalid
36318          * Fires after the field has been marked as invalid.
36319          * @param {Roo.form.Field} this
36320          * @param {String} msg The validation message
36321          */
36322         invalid : true,
36323        /**
36324          * @event valid
36325          * Fires after the field has been validated with no errors.
36326          * @param {Roo.form.Field} this
36327          */
36328         valid : true
36329     });
36330 };
36331
36332 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
36333     
36334     fieldLabel : '',
36335     labelAlign : 'top',
36336     labelWidth : 3,
36337     dayAllowBlank : false,
36338     monthAllowBlank : false,
36339     yearAllowBlank : false,
36340     dayPlaceholder : '',
36341     monthPlaceholder : '',
36342     yearPlaceholder : '',
36343     dayFormat : 'd',
36344     monthFormat : 'm',
36345     yearFormat : 'Y',
36346     isFormField : true,
36347     labellg : 0,
36348     labelmd : 0,
36349     labelsm : 0,
36350     labelxs : 0,
36351     
36352     getAutoCreate : function()
36353     {
36354         var cfg = {
36355             tag : 'div',
36356             cls : 'row roo-date-split-field-group',
36357             cn : [
36358                 {
36359                     tag : 'input',
36360                     type : 'hidden',
36361                     cls : 'form-hidden-field roo-date-split-field-group-value',
36362                     name : this.name
36363                 }
36364             ]
36365         };
36366         
36367         var labelCls = 'col-md-12';
36368         var contentCls = 'col-md-4';
36369         
36370         if(this.fieldLabel){
36371             
36372             var label = {
36373                 tag : 'div',
36374                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
36375                 cn : [
36376                     {
36377                         tag : 'label',
36378                         html : this.fieldLabel
36379                     }
36380                 ]
36381             };
36382             
36383             if(this.labelAlign == 'left'){
36384             
36385                 if(this.labelWidth > 12){
36386                     label.style = "width: " + this.labelWidth + 'px';
36387                 }
36388
36389                 if(this.labelWidth < 13 && this.labelmd == 0){
36390                     this.labelmd = this.labelWidth;
36391                 }
36392
36393                 if(this.labellg > 0){
36394                     labelCls = ' col-lg-' + this.labellg;
36395                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
36396                 }
36397
36398                 if(this.labelmd > 0){
36399                     labelCls = ' col-md-' + this.labelmd;
36400                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
36401                 }
36402
36403                 if(this.labelsm > 0){
36404                     labelCls = ' col-sm-' + this.labelsm;
36405                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
36406                 }
36407
36408                 if(this.labelxs > 0){
36409                     labelCls = ' col-xs-' + this.labelxs;
36410                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
36411                 }
36412             }
36413             
36414             label.cls += ' ' + labelCls;
36415             
36416             cfg.cn.push(label);
36417         }
36418         
36419         Roo.each(['day', 'month', 'year'], function(t){
36420             cfg.cn.push({
36421                 tag : 'div',
36422                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
36423             });
36424         }, this);
36425         
36426         return cfg;
36427     },
36428     
36429     inputEl: function ()
36430     {
36431         return this.el.select('.roo-date-split-field-group-value', true).first();
36432     },
36433     
36434     onRender : function(ct, position) 
36435     {
36436         var _this = this;
36437         
36438         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
36439         
36440         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
36441         
36442         this.dayField = new Roo.bootstrap.form.ComboBox({
36443             allowBlank : this.dayAllowBlank,
36444             alwaysQuery : true,
36445             displayField : 'value',
36446             editable : false,
36447             fieldLabel : '',
36448             forceSelection : true,
36449             mode : 'local',
36450             placeholder : this.dayPlaceholder,
36451             selectOnFocus : true,
36452             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36453             triggerAction : 'all',
36454             typeAhead : true,
36455             valueField : 'value',
36456             store : new Roo.data.SimpleStore({
36457                 data : (function() {    
36458                     var days = [];
36459                     _this.fireEvent('days', _this, days);
36460                     return days;
36461                 })(),
36462                 fields : [ 'value' ]
36463             }),
36464             listeners : {
36465                 select : function (_self, record, index)
36466                 {
36467                     _this.setValue(_this.getValue());
36468                 }
36469             }
36470         });
36471
36472         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
36473         
36474         this.monthField = new Roo.bootstrap.form.MonthField({
36475             after : '<i class=\"fa fa-calendar\"></i>',
36476             allowBlank : this.monthAllowBlank,
36477             placeholder : this.monthPlaceholder,
36478             readOnly : true,
36479             listeners : {
36480                 render : function (_self)
36481                 {
36482                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
36483                         e.preventDefault();
36484                         _self.focus();
36485                     });
36486                 },
36487                 select : function (_self, oldvalue, newvalue)
36488                 {
36489                     _this.setValue(_this.getValue());
36490                 }
36491             }
36492         });
36493         
36494         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
36495         
36496         this.yearField = new Roo.bootstrap.form.ComboBox({
36497             allowBlank : this.yearAllowBlank,
36498             alwaysQuery : true,
36499             displayField : 'value',
36500             editable : false,
36501             fieldLabel : '',
36502             forceSelection : true,
36503             mode : 'local',
36504             placeholder : this.yearPlaceholder,
36505             selectOnFocus : true,
36506             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36507             triggerAction : 'all',
36508             typeAhead : true,
36509             valueField : 'value',
36510             store : new Roo.data.SimpleStore({
36511                 data : (function() {
36512                     var years = [];
36513                     _this.fireEvent('years', _this, years);
36514                     return years;
36515                 })(),
36516                 fields : [ 'value' ]
36517             }),
36518             listeners : {
36519                 select : function (_self, record, index)
36520                 {
36521                     _this.setValue(_this.getValue());
36522                 }
36523             }
36524         });
36525
36526         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
36527     },
36528     
36529     setValue : function(v, format)
36530     {
36531         this.inputEl.dom.value = v;
36532         
36533         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
36534         
36535         var d = Date.parseDate(v, f);
36536         
36537         if(!d){
36538             this.validate();
36539             return;
36540         }
36541         
36542         this.setDay(d.format(this.dayFormat));
36543         this.setMonth(d.format(this.monthFormat));
36544         this.setYear(d.format(this.yearFormat));
36545         
36546         this.validate();
36547         
36548         return;
36549     },
36550     
36551     setDay : function(v)
36552     {
36553         this.dayField.setValue(v);
36554         this.inputEl.dom.value = this.getValue();
36555         this.validate();
36556         return;
36557     },
36558     
36559     setMonth : function(v)
36560     {
36561         this.monthField.setValue(v, true);
36562         this.inputEl.dom.value = this.getValue();
36563         this.validate();
36564         return;
36565     },
36566     
36567     setYear : function(v)
36568     {
36569         this.yearField.setValue(v);
36570         this.inputEl.dom.value = this.getValue();
36571         this.validate();
36572         return;
36573     },
36574     
36575     getDay : function()
36576     {
36577         return this.dayField.getValue();
36578     },
36579     
36580     getMonth : function()
36581     {
36582         return this.monthField.getValue();
36583     },
36584     
36585     getYear : function()
36586     {
36587         return this.yearField.getValue();
36588     },
36589     
36590     getValue : function()
36591     {
36592         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
36593         
36594         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
36595         
36596         return date;
36597     },
36598     
36599     reset : function()
36600     {
36601         this.setDay('');
36602         this.setMonth('');
36603         this.setYear('');
36604         this.inputEl.dom.value = '';
36605         this.validate();
36606         return;
36607     },
36608     
36609     validate : function()
36610     {
36611         var d = this.dayField.validate();
36612         var m = this.monthField.validate();
36613         var y = this.yearField.validate();
36614         
36615         var valid = true;
36616         
36617         if(
36618                 (!this.dayAllowBlank && !d) ||
36619                 (!this.monthAllowBlank && !m) ||
36620                 (!this.yearAllowBlank && !y)
36621         ){
36622             valid = false;
36623         }
36624         
36625         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
36626             return valid;
36627         }
36628         
36629         if(valid){
36630             this.markValid();
36631             return valid;
36632         }
36633         
36634         this.markInvalid();
36635         
36636         return valid;
36637     },
36638     
36639     markValid : function()
36640     {
36641         
36642         var label = this.el.select('label', true).first();
36643         var icon = this.el.select('i.fa-star', true).first();
36644
36645         if(label && icon){
36646             icon.remove();
36647         }
36648         
36649         this.fireEvent('valid', this);
36650     },
36651     
36652      /**
36653      * Mark this field as invalid
36654      * @param {String} msg The validation message
36655      */
36656     markInvalid : function(msg)
36657     {
36658         
36659         var label = this.el.select('label', true).first();
36660         var icon = this.el.select('i.fa-star', true).first();
36661
36662         if(label && !icon){
36663             this.el.select('.roo-date-split-field-label', true).createChild({
36664                 tag : 'i',
36665                 cls : 'text-danger fa fa-lg fa-star',
36666                 tooltip : 'This field is required',
36667                 style : 'margin-right:5px;'
36668             }, label, true);
36669         }
36670         
36671         this.fireEvent('invalid', this, msg);
36672     },
36673     
36674     clearInvalid : function()
36675     {
36676         var label = this.el.select('label', true).first();
36677         var icon = this.el.select('i.fa-star', true).first();
36678
36679         if(label && icon){
36680             icon.remove();
36681         }
36682         
36683         this.fireEvent('valid', this);
36684     },
36685     
36686     getName: function()
36687     {
36688         return this.name;
36689     }
36690     
36691 });
36692
36693  
36694
36695 /**
36696  * @class Roo.bootstrap.LayoutMasonry
36697  * @extends Roo.bootstrap.Component
36698  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
36699  * Bootstrap Layout Masonry class
36700  *
36701  * This is based on 
36702  * http://masonry.desandro.com
36703  *
36704  * The idea is to render all the bricks based on vertical width...
36705  *
36706  * The original code extends 'outlayer' - we might need to use that....
36707
36708  * @constructor
36709  * Create a new Element
36710  * @param {Object} config The config object
36711  */
36712
36713 Roo.bootstrap.LayoutMasonry = function(config){
36714     
36715     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
36716     
36717     this.bricks = [];
36718     
36719     Roo.bootstrap.LayoutMasonry.register(this);
36720     
36721     this.addEvents({
36722         // raw events
36723         /**
36724          * @event layout
36725          * Fire after layout the items
36726          * @param {Roo.bootstrap.LayoutMasonry} this
36727          * @param {Roo.EventObject} e
36728          */
36729         "layout" : true
36730     });
36731     
36732 };
36733
36734 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
36735     
36736     /**
36737      * @cfg {Boolean} isLayoutInstant = no animation?
36738      */   
36739     isLayoutInstant : false, // needed?
36740    
36741     /**
36742      * @cfg {Number} boxWidth  width of the columns
36743      */   
36744     boxWidth : 450,
36745     
36746       /**
36747      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
36748      */   
36749     boxHeight : 0,
36750     
36751     /**
36752      * @cfg {Number} padWidth padding below box..
36753      */   
36754     padWidth : 10, 
36755     
36756     /**
36757      * @cfg {Number} gutter gutter width..
36758      */   
36759     gutter : 10,
36760     
36761      /**
36762      * @cfg {Number} maxCols maximum number of columns
36763      */   
36764     
36765     maxCols: 0,
36766     
36767     /**
36768      * @cfg {Boolean} isAutoInitial defalut true
36769      */   
36770     isAutoInitial : true, 
36771     
36772     containerWidth: 0,
36773     
36774     /**
36775      * @cfg {Boolean} isHorizontal defalut false
36776      */   
36777     isHorizontal : false, 
36778
36779     currentSize : null,
36780     
36781     tag: 'div',
36782     
36783     cls: '',
36784     
36785     bricks: null, //CompositeElement
36786     
36787     cols : 1,
36788     
36789     _isLayoutInited : false,
36790     
36791 //    isAlternative : false, // only use for vertical layout...
36792     
36793     /**
36794      * @cfg {Number} alternativePadWidth padding below box..
36795      */   
36796     alternativePadWidth : 50,
36797     
36798     selectedBrick : [],
36799     
36800     getAutoCreate : function(){
36801         
36802         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
36803         
36804         var cfg = {
36805             tag: this.tag,
36806             cls: 'blog-masonary-wrapper ' + this.cls,
36807             cn : {
36808                 cls : 'mas-boxes masonary'
36809             }
36810         };
36811         
36812         return cfg;
36813     },
36814     
36815     getChildContainer: function( )
36816     {
36817         if (this.boxesEl) {
36818             return this.boxesEl;
36819         }
36820         
36821         this.boxesEl = this.el.select('.mas-boxes').first();
36822         
36823         return this.boxesEl;
36824     },
36825     
36826     
36827     initEvents : function()
36828     {
36829         var _this = this;
36830         
36831         if(this.isAutoInitial){
36832             Roo.log('hook children rendered');
36833             this.on('childrenrendered', function() {
36834                 Roo.log('children rendered');
36835                 _this.initial();
36836             } ,this);
36837         }
36838     },
36839     
36840     initial : function()
36841     {
36842         this.selectedBrick = [];
36843         
36844         this.currentSize = this.el.getBox(true);
36845         
36846         Roo.EventManager.onWindowResize(this.resize, this); 
36847
36848         if(!this.isAutoInitial){
36849             this.layout();
36850             return;
36851         }
36852         
36853         this.layout();
36854         
36855         return;
36856         //this.layout.defer(500,this);
36857         
36858     },
36859     
36860     resize : function()
36861     {
36862         var cs = this.el.getBox(true);
36863         
36864         if (
36865                 this.currentSize.width == cs.width && 
36866                 this.currentSize.x == cs.x && 
36867                 this.currentSize.height == cs.height && 
36868                 this.currentSize.y == cs.y 
36869         ) {
36870             Roo.log("no change in with or X or Y");
36871             return;
36872         }
36873         
36874         this.currentSize = cs;
36875         
36876         this.layout();
36877         
36878     },
36879     
36880     layout : function()
36881     {   
36882         this._resetLayout();
36883         
36884         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
36885         
36886         this.layoutItems( isInstant );
36887       
36888         this._isLayoutInited = true;
36889         
36890         this.fireEvent('layout', this);
36891         
36892     },
36893     
36894     _resetLayout : function()
36895     {
36896         if(this.isHorizontal){
36897             this.horizontalMeasureColumns();
36898             return;
36899         }
36900         
36901         this.verticalMeasureColumns();
36902         
36903     },
36904     
36905     verticalMeasureColumns : function()
36906     {
36907         this.getContainerWidth();
36908         
36909 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
36910 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
36911 //            return;
36912 //        }
36913         
36914         var boxWidth = this.boxWidth + this.padWidth;
36915         
36916         if(this.containerWidth < this.boxWidth){
36917             boxWidth = this.containerWidth
36918         }
36919         
36920         var containerWidth = this.containerWidth;
36921         
36922         var cols = Math.floor(containerWidth / boxWidth);
36923         
36924         this.cols = Math.max( cols, 1 );
36925         
36926         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36927         
36928         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
36929         
36930         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
36931         
36932         this.colWidth = boxWidth + avail - this.padWidth;
36933         
36934         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
36935         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
36936     },
36937     
36938     horizontalMeasureColumns : function()
36939     {
36940         this.getContainerWidth();
36941         
36942         var boxWidth = this.boxWidth;
36943         
36944         if(this.containerWidth < boxWidth){
36945             boxWidth = this.containerWidth;
36946         }
36947         
36948         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
36949         
36950         this.el.setHeight(boxWidth);
36951         
36952     },
36953     
36954     getContainerWidth : function()
36955     {
36956         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36957     },
36958     
36959     layoutItems : function( isInstant )
36960     {
36961         Roo.log(this.bricks);
36962         
36963         var items = Roo.apply([], this.bricks);
36964         
36965         if(this.isHorizontal){
36966             this._horizontalLayoutItems( items , isInstant );
36967             return;
36968         }
36969         
36970 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
36971 //            this._verticalAlternativeLayoutItems( items , isInstant );
36972 //            return;
36973 //        }
36974         
36975         this._verticalLayoutItems( items , isInstant );
36976         
36977     },
36978     
36979     _verticalLayoutItems : function ( items , isInstant)
36980     {
36981         if ( !items || !items.length ) {
36982             return;
36983         }
36984         
36985         var standard = [
36986             ['xs', 'xs', 'xs', 'tall'],
36987             ['xs', 'xs', 'tall'],
36988             ['xs', 'xs', 'sm'],
36989             ['xs', 'xs', 'xs'],
36990             ['xs', 'tall'],
36991             ['xs', 'sm'],
36992             ['xs', 'xs'],
36993             ['xs'],
36994             
36995             ['sm', 'xs', 'xs'],
36996             ['sm', 'xs'],
36997             ['sm'],
36998             
36999             ['tall', 'xs', 'xs', 'xs'],
37000             ['tall', 'xs', 'xs'],
37001             ['tall', 'xs'],
37002             ['tall']
37003             
37004         ];
37005         
37006         var queue = [];
37007         
37008         var boxes = [];
37009         
37010         var box = [];
37011         
37012         Roo.each(items, function(item, k){
37013             
37014             switch (item.size) {
37015                 // these layouts take up a full box,
37016                 case 'md' :
37017                 case 'md-left' :
37018                 case 'md-right' :
37019                 case 'wide' :
37020                     
37021                     if(box.length){
37022                         boxes.push(box);
37023                         box = [];
37024                     }
37025                     
37026                     boxes.push([item]);
37027                     
37028                     break;
37029                     
37030                 case 'xs' :
37031                 case 'sm' :
37032                 case 'tall' :
37033                     
37034                     box.push(item);
37035                     
37036                     break;
37037                 default :
37038                     break;
37039                     
37040             }
37041             
37042         }, this);
37043         
37044         if(box.length){
37045             boxes.push(box);
37046             box = [];
37047         }
37048         
37049         var filterPattern = function(box, length)
37050         {
37051             if(!box.length){
37052                 return;
37053             }
37054             
37055             var match = false;
37056             
37057             var pattern = box.slice(0, length);
37058             
37059             var format = [];
37060             
37061             Roo.each(pattern, function(i){
37062                 format.push(i.size);
37063             }, this);
37064             
37065             Roo.each(standard, function(s){
37066                 
37067                 if(String(s) != String(format)){
37068                     return;
37069                 }
37070                 
37071                 match = true;
37072                 return false;
37073                 
37074             }, this);
37075             
37076             if(!match && length == 1){
37077                 return;
37078             }
37079             
37080             if(!match){
37081                 filterPattern(box, length - 1);
37082                 return;
37083             }
37084                 
37085             queue.push(pattern);
37086
37087             box = box.slice(length, box.length);
37088
37089             filterPattern(box, 4);
37090
37091             return;
37092             
37093         }
37094         
37095         Roo.each(boxes, function(box, k){
37096             
37097             if(!box.length){
37098                 return;
37099             }
37100             
37101             if(box.length == 1){
37102                 queue.push(box);
37103                 return;
37104             }
37105             
37106             filterPattern(box, 4);
37107             
37108         }, this);
37109         
37110         this._processVerticalLayoutQueue( queue, isInstant );
37111         
37112     },
37113     
37114 //    _verticalAlternativeLayoutItems : function( items , isInstant )
37115 //    {
37116 //        if ( !items || !items.length ) {
37117 //            return;
37118 //        }
37119 //
37120 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
37121 //        
37122 //    },
37123     
37124     _horizontalLayoutItems : function ( items , isInstant)
37125     {
37126         if ( !items || !items.length || items.length < 3) {
37127             return;
37128         }
37129         
37130         items.reverse();
37131         
37132         var eItems = items.slice(0, 3);
37133         
37134         items = items.slice(3, items.length);
37135         
37136         var standard = [
37137             ['xs', 'xs', 'xs', 'wide'],
37138             ['xs', 'xs', 'wide'],
37139             ['xs', 'xs', 'sm'],
37140             ['xs', 'xs', 'xs'],
37141             ['xs', 'wide'],
37142             ['xs', 'sm'],
37143             ['xs', 'xs'],
37144             ['xs'],
37145             
37146             ['sm', 'xs', 'xs'],
37147             ['sm', 'xs'],
37148             ['sm'],
37149             
37150             ['wide', 'xs', 'xs', 'xs'],
37151             ['wide', 'xs', 'xs'],
37152             ['wide', 'xs'],
37153             ['wide'],
37154             
37155             ['wide-thin']
37156         ];
37157         
37158         var queue = [];
37159         
37160         var boxes = [];
37161         
37162         var box = [];
37163         
37164         Roo.each(items, function(item, k){
37165             
37166             switch (item.size) {
37167                 case 'md' :
37168                 case 'md-left' :
37169                 case 'md-right' :
37170                 case 'tall' :
37171                     
37172                     if(box.length){
37173                         boxes.push(box);
37174                         box = [];
37175                     }
37176                     
37177                     boxes.push([item]);
37178                     
37179                     break;
37180                     
37181                 case 'xs' :
37182                 case 'sm' :
37183                 case 'wide' :
37184                 case 'wide-thin' :
37185                     
37186                     box.push(item);
37187                     
37188                     break;
37189                 default :
37190                     break;
37191                     
37192             }
37193             
37194         }, this);
37195         
37196         if(box.length){
37197             boxes.push(box);
37198             box = [];
37199         }
37200         
37201         var filterPattern = function(box, length)
37202         {
37203             if(!box.length){
37204                 return;
37205             }
37206             
37207             var match = false;
37208             
37209             var pattern = box.slice(0, length);
37210             
37211             var format = [];
37212             
37213             Roo.each(pattern, function(i){
37214                 format.push(i.size);
37215             }, this);
37216             
37217             Roo.each(standard, function(s){
37218                 
37219                 if(String(s) != String(format)){
37220                     return;
37221                 }
37222                 
37223                 match = true;
37224                 return false;
37225                 
37226             }, this);
37227             
37228             if(!match && length == 1){
37229                 return;
37230             }
37231             
37232             if(!match){
37233                 filterPattern(box, length - 1);
37234                 return;
37235             }
37236                 
37237             queue.push(pattern);
37238
37239             box = box.slice(length, box.length);
37240
37241             filterPattern(box, 4);
37242
37243             return;
37244             
37245         }
37246         
37247         Roo.each(boxes, function(box, k){
37248             
37249             if(!box.length){
37250                 return;
37251             }
37252             
37253             if(box.length == 1){
37254                 queue.push(box);
37255                 return;
37256             }
37257             
37258             filterPattern(box, 4);
37259             
37260         }, this);
37261         
37262         
37263         var prune = [];
37264         
37265         var pos = this.el.getBox(true);
37266         
37267         var minX = pos.x;
37268         
37269         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37270         
37271         var hit_end = false;
37272         
37273         Roo.each(queue, function(box){
37274             
37275             if(hit_end){
37276                 
37277                 Roo.each(box, function(b){
37278                 
37279                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
37280                     b.el.hide();
37281
37282                 }, this);
37283
37284                 return;
37285             }
37286             
37287             var mx = 0;
37288             
37289             Roo.each(box, function(b){
37290                 
37291                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
37292                 b.el.show();
37293
37294                 mx = Math.max(mx, b.x);
37295                 
37296             }, this);
37297             
37298             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
37299             
37300             if(maxX < minX){
37301                 
37302                 Roo.each(box, function(b){
37303                 
37304                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
37305                     b.el.hide();
37306                     
37307                 }, this);
37308                 
37309                 hit_end = true;
37310                 
37311                 return;
37312             }
37313             
37314             prune.push(box);
37315             
37316         }, this);
37317         
37318         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
37319     },
37320     
37321     /** Sets position of item in DOM
37322     * @param {Element} item
37323     * @param {Number} x - horizontal position
37324     * @param {Number} y - vertical position
37325     * @param {Boolean} isInstant - disables transitions
37326     */
37327     _processVerticalLayoutQueue : function( queue, isInstant )
37328     {
37329         var pos = this.el.getBox(true);
37330         var x = pos.x;
37331         var y = pos.y;
37332         var maxY = [];
37333         
37334         for (var i = 0; i < this.cols; i++){
37335             maxY[i] = pos.y;
37336         }
37337         
37338         Roo.each(queue, function(box, k){
37339             
37340             var col = k % this.cols;
37341             
37342             Roo.each(box, function(b,kk){
37343                 
37344                 b.el.position('absolute');
37345                 
37346                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37347                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37348                 
37349                 if(b.size == 'md-left' || b.size == 'md-right'){
37350                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37351                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37352                 }
37353                 
37354                 b.el.setWidth(width);
37355                 b.el.setHeight(height);
37356                 // iframe?
37357                 b.el.select('iframe',true).setSize(width,height);
37358                 
37359             }, this);
37360             
37361             for (var i = 0; i < this.cols; i++){
37362                 
37363                 if(maxY[i] < maxY[col]){
37364                     col = i;
37365                     continue;
37366                 }
37367                 
37368                 col = Math.min(col, i);
37369                 
37370             }
37371             
37372             x = pos.x + col * (this.colWidth + this.padWidth);
37373             
37374             y = maxY[col];
37375             
37376             var positions = [];
37377             
37378             switch (box.length){
37379                 case 1 :
37380                     positions = this.getVerticalOneBoxColPositions(x, y, box);
37381                     break;
37382                 case 2 :
37383                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
37384                     break;
37385                 case 3 :
37386                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
37387                     break;
37388                 case 4 :
37389                     positions = this.getVerticalFourBoxColPositions(x, y, box);
37390                     break;
37391                 default :
37392                     break;
37393             }
37394             
37395             Roo.each(box, function(b,kk){
37396                 
37397                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37398                 
37399                 var sz = b.el.getSize();
37400                 
37401                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
37402                 
37403             }, this);
37404             
37405         }, this);
37406         
37407         var mY = 0;
37408         
37409         for (var i = 0; i < this.cols; i++){
37410             mY = Math.max(mY, maxY[i]);
37411         }
37412         
37413         this.el.setHeight(mY - pos.y);
37414         
37415     },
37416     
37417 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
37418 //    {
37419 //        var pos = this.el.getBox(true);
37420 //        var x = pos.x;
37421 //        var y = pos.y;
37422 //        var maxX = pos.right;
37423 //        
37424 //        var maxHeight = 0;
37425 //        
37426 //        Roo.each(items, function(item, k){
37427 //            
37428 //            var c = k % 2;
37429 //            
37430 //            item.el.position('absolute');
37431 //                
37432 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
37433 //
37434 //            item.el.setWidth(width);
37435 //
37436 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
37437 //
37438 //            item.el.setHeight(height);
37439 //            
37440 //            if(c == 0){
37441 //                item.el.setXY([x, y], isInstant ? false : true);
37442 //            } else {
37443 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
37444 //            }
37445 //            
37446 //            y = y + height + this.alternativePadWidth;
37447 //            
37448 //            maxHeight = maxHeight + height + this.alternativePadWidth;
37449 //            
37450 //        }, this);
37451 //        
37452 //        this.el.setHeight(maxHeight);
37453 //        
37454 //    },
37455     
37456     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
37457     {
37458         var pos = this.el.getBox(true);
37459         
37460         var minX = pos.x;
37461         var minY = pos.y;
37462         
37463         var maxX = pos.right;
37464         
37465         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
37466         
37467         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37468         
37469         Roo.each(queue, function(box, k){
37470             
37471             Roo.each(box, function(b, kk){
37472                 
37473                 b.el.position('absolute');
37474                 
37475                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37476                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37477                 
37478                 if(b.size == 'md-left' || b.size == 'md-right'){
37479                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37480                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37481                 }
37482                 
37483                 b.el.setWidth(width);
37484                 b.el.setHeight(height);
37485                 
37486             }, this);
37487             
37488             if(!box.length){
37489                 return;
37490             }
37491             
37492             var positions = [];
37493             
37494             switch (box.length){
37495                 case 1 :
37496                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
37497                     break;
37498                 case 2 :
37499                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
37500                     break;
37501                 case 3 :
37502                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
37503                     break;
37504                 case 4 :
37505                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
37506                     break;
37507                 default :
37508                     break;
37509             }
37510             
37511             Roo.each(box, function(b,kk){
37512                 
37513                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37514                 
37515                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
37516                 
37517             }, this);
37518             
37519         }, this);
37520         
37521     },
37522     
37523     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
37524     {
37525         Roo.each(eItems, function(b,k){
37526             
37527             b.size = (k == 0) ? 'sm' : 'xs';
37528             b.x = (k == 0) ? 2 : 1;
37529             b.y = (k == 0) ? 2 : 1;
37530             
37531             b.el.position('absolute');
37532             
37533             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37534                 
37535             b.el.setWidth(width);
37536             
37537             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37538             
37539             b.el.setHeight(height);
37540             
37541         }, this);
37542
37543         var positions = [];
37544         
37545         positions.push({
37546             x : maxX - this.unitWidth * 2 - this.gutter,
37547             y : minY
37548         });
37549         
37550         positions.push({
37551             x : maxX - this.unitWidth,
37552             y : minY + (this.unitWidth + this.gutter) * 2
37553         });
37554         
37555         positions.push({
37556             x : maxX - this.unitWidth * 3 - this.gutter * 2,
37557             y : minY
37558         });
37559         
37560         Roo.each(eItems, function(b,k){
37561             
37562             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
37563
37564         }, this);
37565         
37566     },
37567     
37568     getVerticalOneBoxColPositions : function(x, y, box)
37569     {
37570         var pos = [];
37571         
37572         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
37573         
37574         if(box[0].size == 'md-left'){
37575             rand = 0;
37576         }
37577         
37578         if(box[0].size == 'md-right'){
37579             rand = 1;
37580         }
37581         
37582         pos.push({
37583             x : x + (this.unitWidth + this.gutter) * rand,
37584             y : y
37585         });
37586         
37587         return pos;
37588     },
37589     
37590     getVerticalTwoBoxColPositions : function(x, y, box)
37591     {
37592         var pos = [];
37593         
37594         if(box[0].size == 'xs'){
37595             
37596             pos.push({
37597                 x : x,
37598                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
37599             });
37600
37601             pos.push({
37602                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
37603                 y : y
37604             });
37605             
37606             return pos;
37607             
37608         }
37609         
37610         pos.push({
37611             x : x,
37612             y : y
37613         });
37614
37615         pos.push({
37616             x : x + (this.unitWidth + this.gutter) * 2,
37617             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
37618         });
37619         
37620         return pos;
37621         
37622     },
37623     
37624     getVerticalThreeBoxColPositions : function(x, y, box)
37625     {
37626         var pos = [];
37627         
37628         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
37629             
37630             pos.push({
37631                 x : x,
37632                 y : y
37633             });
37634
37635             pos.push({
37636                 x : x + (this.unitWidth + this.gutter) * 1,
37637                 y : y
37638             });
37639             
37640             pos.push({
37641                 x : x + (this.unitWidth + this.gutter) * 2,
37642                 y : y
37643             });
37644             
37645             return pos;
37646             
37647         }
37648         
37649         if(box[0].size == 'xs' && box[1].size == 'xs'){
37650             
37651             pos.push({
37652                 x : x,
37653                 y : y
37654             });
37655
37656             pos.push({
37657                 x : x,
37658                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
37659             });
37660             
37661             pos.push({
37662                 x : x + (this.unitWidth + this.gutter) * 1,
37663                 y : y
37664             });
37665             
37666             return pos;
37667             
37668         }
37669         
37670         pos.push({
37671             x : x,
37672             y : y
37673         });
37674
37675         pos.push({
37676             x : x + (this.unitWidth + this.gutter) * 2,
37677             y : y
37678         });
37679
37680         pos.push({
37681             x : x + (this.unitWidth + this.gutter) * 2,
37682             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
37683         });
37684             
37685         return pos;
37686         
37687     },
37688     
37689     getVerticalFourBoxColPositions : function(x, y, box)
37690     {
37691         var pos = [];
37692         
37693         if(box[0].size == 'xs'){
37694             
37695             pos.push({
37696                 x : x,
37697                 y : y
37698             });
37699
37700             pos.push({
37701                 x : x,
37702                 y : y + (this.unitHeight + this.gutter) * 1
37703             });
37704             
37705             pos.push({
37706                 x : x,
37707                 y : y + (this.unitHeight + this.gutter) * 2
37708             });
37709             
37710             pos.push({
37711                 x : x + (this.unitWidth + this.gutter) * 1,
37712                 y : y
37713             });
37714             
37715             return pos;
37716             
37717         }
37718         
37719         pos.push({
37720             x : x,
37721             y : y
37722         });
37723
37724         pos.push({
37725             x : x + (this.unitWidth + this.gutter) * 2,
37726             y : y
37727         });
37728
37729         pos.push({
37730             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
37731             y : y + (this.unitHeight + this.gutter) * 1
37732         });
37733
37734         pos.push({
37735             x : x + (this.unitWidth + this.gutter) * 2,
37736             y : y + (this.unitWidth + this.gutter) * 2
37737         });
37738
37739         return pos;
37740         
37741     },
37742     
37743     getHorizontalOneBoxColPositions : function(maxX, minY, box)
37744     {
37745         var pos = [];
37746         
37747         if(box[0].size == 'md-left'){
37748             pos.push({
37749                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37750                 y : minY
37751             });
37752             
37753             return pos;
37754         }
37755         
37756         if(box[0].size == 'md-right'){
37757             pos.push({
37758                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37759                 y : minY + (this.unitWidth + this.gutter) * 1
37760             });
37761             
37762             return pos;
37763         }
37764         
37765         var rand = Math.floor(Math.random() * (4 - box[0].y));
37766         
37767         pos.push({
37768             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37769             y : minY + (this.unitWidth + this.gutter) * rand
37770         });
37771         
37772         return pos;
37773         
37774     },
37775     
37776     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
37777     {
37778         var pos = [];
37779         
37780         if(box[0].size == 'xs'){
37781             
37782             pos.push({
37783                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37784                 y : minY
37785             });
37786
37787             pos.push({
37788                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37789                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
37790             });
37791             
37792             return pos;
37793             
37794         }
37795         
37796         pos.push({
37797             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37798             y : minY
37799         });
37800
37801         pos.push({
37802             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37803             y : minY + (this.unitWidth + this.gutter) * 2
37804         });
37805         
37806         return pos;
37807         
37808     },
37809     
37810     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
37811     {
37812         var pos = [];
37813         
37814         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
37815             
37816             pos.push({
37817                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37818                 y : minY
37819             });
37820
37821             pos.push({
37822                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37823                 y : minY + (this.unitWidth + this.gutter) * 1
37824             });
37825             
37826             pos.push({
37827                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37828                 y : minY + (this.unitWidth + this.gutter) * 2
37829             });
37830             
37831             return pos;
37832             
37833         }
37834         
37835         if(box[0].size == 'xs' && box[1].size == 'xs'){
37836             
37837             pos.push({
37838                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37839                 y : minY
37840             });
37841
37842             pos.push({
37843                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37844                 y : minY
37845             });
37846             
37847             pos.push({
37848                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37849                 y : minY + (this.unitWidth + this.gutter) * 1
37850             });
37851             
37852             return pos;
37853             
37854         }
37855         
37856         pos.push({
37857             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37858             y : minY
37859         });
37860
37861         pos.push({
37862             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37863             y : minY + (this.unitWidth + this.gutter) * 2
37864         });
37865
37866         pos.push({
37867             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37868             y : minY + (this.unitWidth + this.gutter) * 2
37869         });
37870             
37871         return pos;
37872         
37873     },
37874     
37875     getHorizontalFourBoxColPositions : function(maxX, minY, box)
37876     {
37877         var pos = [];
37878         
37879         if(box[0].size == 'xs'){
37880             
37881             pos.push({
37882                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37883                 y : minY
37884             });
37885
37886             pos.push({
37887                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37888                 y : minY
37889             });
37890             
37891             pos.push({
37892                 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),
37893                 y : minY
37894             });
37895             
37896             pos.push({
37897                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
37898                 y : minY + (this.unitWidth + this.gutter) * 1
37899             });
37900             
37901             return pos;
37902             
37903         }
37904         
37905         pos.push({
37906             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37907             y : minY
37908         });
37909         
37910         pos.push({
37911             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37912             y : minY + (this.unitWidth + this.gutter) * 2
37913         });
37914         
37915         pos.push({
37916             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37917             y : minY + (this.unitWidth + this.gutter) * 2
37918         });
37919         
37920         pos.push({
37921             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),
37922             y : minY + (this.unitWidth + this.gutter) * 2
37923         });
37924
37925         return pos;
37926         
37927     },
37928     
37929     /**
37930     * remove a Masonry Brick
37931     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
37932     */
37933     removeBrick : function(brick_id)
37934     {
37935         if (!brick_id) {
37936             return;
37937         }
37938         
37939         for (var i = 0; i<this.bricks.length; i++) {
37940             if (this.bricks[i].id == brick_id) {
37941                 this.bricks.splice(i,1);
37942                 this.el.dom.removeChild(Roo.get(brick_id).dom);
37943                 this.initial();
37944             }
37945         }
37946     },
37947     
37948     /**
37949     * adds a Masonry Brick
37950     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
37951     */
37952     addBrick : function(cfg)
37953     {
37954         var cn = new Roo.bootstrap.MasonryBrick(cfg);
37955         //this.register(cn);
37956         cn.parentId = this.id;
37957         cn.render(this.el);
37958         return cn;
37959     },
37960     
37961     /**
37962     * register a Masonry Brick
37963     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
37964     */
37965     
37966     register : function(brick)
37967     {
37968         this.bricks.push(brick);
37969         brick.masonryId = this.id;
37970     },
37971     
37972     /**
37973     * clear all the Masonry Brick
37974     */
37975     clearAll : function()
37976     {
37977         this.bricks = [];
37978         //this.getChildContainer().dom.innerHTML = "";
37979         this.el.dom.innerHTML = '';
37980     },
37981     
37982     getSelected : function()
37983     {
37984         if (!this.selectedBrick) {
37985             return false;
37986         }
37987         
37988         return this.selectedBrick;
37989     }
37990 });
37991
37992 Roo.apply(Roo.bootstrap.LayoutMasonry, {
37993     
37994     groups: {},
37995      /**
37996     * register a Masonry Layout
37997     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
37998     */
37999     
38000     register : function(layout)
38001     {
38002         this.groups[layout.id] = layout;
38003     },
38004     /**
38005     * fetch a  Masonry Layout based on the masonry layout ID
38006     * @param {string} the masonry layout to add
38007     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
38008     */
38009     
38010     get: function(layout_id) {
38011         if (typeof(this.groups[layout_id]) == 'undefined') {
38012             return false;
38013         }
38014         return this.groups[layout_id] ;
38015     }
38016     
38017     
38018     
38019 });
38020
38021  
38022
38023  /**
38024  *
38025  * This is based on 
38026  * http://masonry.desandro.com
38027  *
38028  * The idea is to render all the bricks based on vertical width...
38029  *
38030  * The original code extends 'outlayer' - we might need to use that....
38031  * 
38032  */
38033
38034
38035 /**
38036  * @class Roo.bootstrap.LayoutMasonryAuto
38037  * @extends Roo.bootstrap.Component
38038  * Bootstrap Layout Masonry class
38039  * 
38040  * @constructor
38041  * Create a new Element
38042  * @param {Object} config The config object
38043  */
38044
38045 Roo.bootstrap.LayoutMasonryAuto = function(config){
38046     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
38047 };
38048
38049 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
38050     
38051       /**
38052      * @cfg {Boolean} isFitWidth  - resize the width..
38053      */   
38054     isFitWidth : false,  // options..
38055     /**
38056      * @cfg {Boolean} isOriginLeft = left align?
38057      */   
38058     isOriginLeft : true,
38059     /**
38060      * @cfg {Boolean} isOriginTop = top align?
38061      */   
38062     isOriginTop : false,
38063     /**
38064      * @cfg {Boolean} isLayoutInstant = no animation?
38065      */   
38066     isLayoutInstant : false, // needed?
38067     /**
38068      * @cfg {Boolean} isResizingContainer = not sure if this is used..
38069      */   
38070     isResizingContainer : true,
38071     /**
38072      * @cfg {Number} columnWidth  width of the columns 
38073      */   
38074     
38075     columnWidth : 0,
38076     
38077     /**
38078      * @cfg {Number} maxCols maximum number of columns
38079      */   
38080     
38081     maxCols: 0,
38082     /**
38083      * @cfg {Number} padHeight padding below box..
38084      */   
38085     
38086     padHeight : 10, 
38087     
38088     /**
38089      * @cfg {Boolean} isAutoInitial defalut true
38090      */   
38091     
38092     isAutoInitial : true, 
38093     
38094     // private?
38095     gutter : 0,
38096     
38097     containerWidth: 0,
38098     initialColumnWidth : 0,
38099     currentSize : null,
38100     
38101     colYs : null, // array.
38102     maxY : 0,
38103     padWidth: 10,
38104     
38105     
38106     tag: 'div',
38107     cls: '',
38108     bricks: null, //CompositeElement
38109     cols : 0, // array?
38110     // element : null, // wrapped now this.el
38111     _isLayoutInited : null, 
38112     
38113     
38114     getAutoCreate : function(){
38115         
38116         var cfg = {
38117             tag: this.tag,
38118             cls: 'blog-masonary-wrapper ' + this.cls,
38119             cn : {
38120                 cls : 'mas-boxes masonary'
38121             }
38122         };
38123         
38124         return cfg;
38125     },
38126     
38127     getChildContainer: function( )
38128     {
38129         if (this.boxesEl) {
38130             return this.boxesEl;
38131         }
38132         
38133         this.boxesEl = this.el.select('.mas-boxes').first();
38134         
38135         return this.boxesEl;
38136     },
38137     
38138     
38139     initEvents : function()
38140     {
38141         var _this = this;
38142         
38143         if(this.isAutoInitial){
38144             Roo.log('hook children rendered');
38145             this.on('childrenrendered', function() {
38146                 Roo.log('children rendered');
38147                 _this.initial();
38148             } ,this);
38149         }
38150         
38151     },
38152     
38153     initial : function()
38154     {
38155         this.reloadItems();
38156
38157         this.currentSize = this.el.getBox(true);
38158
38159         /// was window resize... - let's see if this works..
38160         Roo.EventManager.onWindowResize(this.resize, this); 
38161
38162         if(!this.isAutoInitial){
38163             this.layout();
38164             return;
38165         }
38166         
38167         this.layout.defer(500,this);
38168     },
38169     
38170     reloadItems: function()
38171     {
38172         this.bricks = this.el.select('.masonry-brick', true);
38173         
38174         this.bricks.each(function(b) {
38175             //Roo.log(b.getSize());
38176             if (!b.attr('originalwidth')) {
38177                 b.attr('originalwidth',  b.getSize().width);
38178             }
38179             
38180         });
38181         
38182         Roo.log(this.bricks.elements.length);
38183     },
38184     
38185     resize : function()
38186     {
38187         Roo.log('resize');
38188         var cs = this.el.getBox(true);
38189         
38190         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
38191             Roo.log("no change in with or X");
38192             return;
38193         }
38194         this.currentSize = cs;
38195         this.layout();
38196     },
38197     
38198     layout : function()
38199     {
38200          Roo.log('layout');
38201         this._resetLayout();
38202         //this._manageStamps();
38203       
38204         // don't animate first layout
38205         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38206         this.layoutItems( isInstant );
38207       
38208         // flag for initalized
38209         this._isLayoutInited = true;
38210     },
38211     
38212     layoutItems : function( isInstant )
38213     {
38214         //var items = this._getItemsForLayout( this.items );
38215         // original code supports filtering layout items.. we just ignore it..
38216         
38217         this._layoutItems( this.bricks , isInstant );
38218       
38219         this._postLayout();
38220     },
38221     _layoutItems : function ( items , isInstant)
38222     {
38223        //this.fireEvent( 'layout', this, items );
38224     
38225
38226         if ( !items || !items.elements.length ) {
38227           // no items, emit event with empty array
38228             return;
38229         }
38230
38231         var queue = [];
38232         items.each(function(item) {
38233             Roo.log("layout item");
38234             Roo.log(item);
38235             // get x/y object from method
38236             var position = this._getItemLayoutPosition( item );
38237             // enqueue
38238             position.item = item;
38239             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
38240             queue.push( position );
38241         }, this);
38242       
38243         this._processLayoutQueue( queue );
38244     },
38245     /** Sets position of item in DOM
38246     * @param {Element} item
38247     * @param {Number} x - horizontal position
38248     * @param {Number} y - vertical position
38249     * @param {Boolean} isInstant - disables transitions
38250     */
38251     _processLayoutQueue : function( queue )
38252     {
38253         for ( var i=0, len = queue.length; i < len; i++ ) {
38254             var obj = queue[i];
38255             obj.item.position('absolute');
38256             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
38257         }
38258     },
38259       
38260     
38261     /**
38262     * Any logic you want to do after each layout,
38263     * i.e. size the container
38264     */
38265     _postLayout : function()
38266     {
38267         this.resizeContainer();
38268     },
38269     
38270     resizeContainer : function()
38271     {
38272         if ( !this.isResizingContainer ) {
38273             return;
38274         }
38275         var size = this._getContainerSize();
38276         if ( size ) {
38277             this.el.setSize(size.width,size.height);
38278             this.boxesEl.setSize(size.width,size.height);
38279         }
38280     },
38281     
38282     
38283     
38284     _resetLayout : function()
38285     {
38286         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
38287         this.colWidth = this.el.getWidth();
38288         //this.gutter = this.el.getWidth(); 
38289         
38290         this.measureColumns();
38291
38292         // reset column Y
38293         var i = this.cols;
38294         this.colYs = [];
38295         while (i--) {
38296             this.colYs.push( 0 );
38297         }
38298     
38299         this.maxY = 0;
38300     },
38301
38302     measureColumns : function()
38303     {
38304         this.getContainerWidth();
38305       // if columnWidth is 0, default to outerWidth of first item
38306         if ( !this.columnWidth ) {
38307             var firstItem = this.bricks.first();
38308             Roo.log(firstItem);
38309             this.columnWidth  = this.containerWidth;
38310             if (firstItem && firstItem.attr('originalwidth') ) {
38311                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
38312             }
38313             // columnWidth fall back to item of first element
38314             Roo.log("set column width?");
38315                         this.initialColumnWidth = this.columnWidth  ;
38316
38317             // if first elem has no width, default to size of container
38318             
38319         }
38320         
38321         
38322         if (this.initialColumnWidth) {
38323             this.columnWidth = this.initialColumnWidth;
38324         }
38325         
38326         
38327             
38328         // column width is fixed at the top - however if container width get's smaller we should
38329         // reduce it...
38330         
38331         // this bit calcs how man columns..
38332             
38333         var columnWidth = this.columnWidth += this.gutter;
38334       
38335         // calculate columns
38336         var containerWidth = this.containerWidth + this.gutter;
38337         
38338         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
38339         // fix rounding errors, typically with gutters
38340         var excess = columnWidth - containerWidth % columnWidth;
38341         
38342         
38343         // if overshoot is less than a pixel, round up, otherwise floor it
38344         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
38345         cols = Math[ mathMethod ]( cols );
38346         this.cols = Math.max( cols, 1 );
38347         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38348         
38349          // padding positioning..
38350         var totalColWidth = this.cols * this.columnWidth;
38351         var padavail = this.containerWidth - totalColWidth;
38352         // so for 2 columns - we need 3 'pads'
38353         
38354         var padNeeded = (1+this.cols) * this.padWidth;
38355         
38356         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
38357         
38358         this.columnWidth += padExtra
38359         //this.padWidth = Math.floor(padavail /  ( this.cols));
38360         
38361         // adjust colum width so that padding is fixed??
38362         
38363         // we have 3 columns ... total = width * 3
38364         // we have X left over... that should be used by 
38365         
38366         //if (this.expandC) {
38367             
38368         //}
38369         
38370         
38371         
38372     },
38373     
38374     getContainerWidth : function()
38375     {
38376        /* // container is parent if fit width
38377         var container = this.isFitWidth ? this.element.parentNode : this.element;
38378         // check that this.size and size are there
38379         // IE8 triggers resize on body size change, so they might not be
38380         
38381         var size = getSize( container );  //FIXME
38382         this.containerWidth = size && size.innerWidth; //FIXME
38383         */
38384          
38385         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
38386         
38387     },
38388     
38389     _getItemLayoutPosition : function( item )  // what is item?
38390     {
38391         // we resize the item to our columnWidth..
38392       
38393         item.setWidth(this.columnWidth);
38394         item.autoBoxAdjust  = false;
38395         
38396         var sz = item.getSize();
38397  
38398         // how many columns does this brick span
38399         var remainder = this.containerWidth % this.columnWidth;
38400         
38401         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
38402         // round if off by 1 pixel, otherwise use ceil
38403         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
38404         colSpan = Math.min( colSpan, this.cols );
38405         
38406         // normally this should be '1' as we dont' currently allow multi width columns..
38407         
38408         var colGroup = this._getColGroup( colSpan );
38409         // get the minimum Y value from the columns
38410         var minimumY = Math.min.apply( Math, colGroup );
38411         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
38412         
38413         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
38414          
38415         // position the brick
38416         var position = {
38417             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
38418             y: this.currentSize.y + minimumY + this.padHeight
38419         };
38420         
38421         Roo.log(position);
38422         // apply setHeight to necessary columns
38423         var setHeight = minimumY + sz.height + this.padHeight;
38424         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
38425         
38426         var setSpan = this.cols + 1 - colGroup.length;
38427         for ( var i = 0; i < setSpan; i++ ) {
38428           this.colYs[ shortColIndex + i ] = setHeight ;
38429         }
38430       
38431         return position;
38432     },
38433     
38434     /**
38435      * @param {Number} colSpan - number of columns the element spans
38436      * @returns {Array} colGroup
38437      */
38438     _getColGroup : function( colSpan )
38439     {
38440         if ( colSpan < 2 ) {
38441           // if brick spans only one column, use all the column Ys
38442           return this.colYs;
38443         }
38444       
38445         var colGroup = [];
38446         // how many different places could this brick fit horizontally
38447         var groupCount = this.cols + 1 - colSpan;
38448         // for each group potential horizontal position
38449         for ( var i = 0; i < groupCount; i++ ) {
38450           // make an array of colY values for that one group
38451           var groupColYs = this.colYs.slice( i, i + colSpan );
38452           // and get the max value of the array
38453           colGroup[i] = Math.max.apply( Math, groupColYs );
38454         }
38455         return colGroup;
38456     },
38457     /*
38458     _manageStamp : function( stamp )
38459     {
38460         var stampSize =  stamp.getSize();
38461         var offset = stamp.getBox();
38462         // get the columns that this stamp affects
38463         var firstX = this.isOriginLeft ? offset.x : offset.right;
38464         var lastX = firstX + stampSize.width;
38465         var firstCol = Math.floor( firstX / this.columnWidth );
38466         firstCol = Math.max( 0, firstCol );
38467         
38468         var lastCol = Math.floor( lastX / this.columnWidth );
38469         // lastCol should not go over if multiple of columnWidth #425
38470         lastCol -= lastX % this.columnWidth ? 0 : 1;
38471         lastCol = Math.min( this.cols - 1, lastCol );
38472         
38473         // set colYs to bottom of the stamp
38474         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
38475             stampSize.height;
38476             
38477         for ( var i = firstCol; i <= lastCol; i++ ) {
38478           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
38479         }
38480     },
38481     */
38482     
38483     _getContainerSize : function()
38484     {
38485         this.maxY = Math.max.apply( Math, this.colYs );
38486         var size = {
38487             height: this.maxY
38488         };
38489       
38490         if ( this.isFitWidth ) {
38491             size.width = this._getContainerFitWidth();
38492         }
38493       
38494         return size;
38495     },
38496     
38497     _getContainerFitWidth : function()
38498     {
38499         var unusedCols = 0;
38500         // count unused columns
38501         var i = this.cols;
38502         while ( --i ) {
38503           if ( this.colYs[i] !== 0 ) {
38504             break;
38505           }
38506           unusedCols++;
38507         }
38508         // fit container to columns that have been used
38509         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
38510     },
38511     
38512     needsResizeLayout : function()
38513     {
38514         var previousWidth = this.containerWidth;
38515         this.getContainerWidth();
38516         return previousWidth !== this.containerWidth;
38517     }
38518  
38519 });
38520
38521  
38522
38523  /*
38524  * - LGPL
38525  *
38526  * element
38527  * 
38528  */
38529
38530 /**
38531  * @class Roo.bootstrap.MasonryBrick
38532  * @extends Roo.bootstrap.Component
38533  * Bootstrap MasonryBrick class
38534  * 
38535  * @constructor
38536  * Create a new MasonryBrick
38537  * @param {Object} config The config object
38538  */
38539
38540 Roo.bootstrap.MasonryBrick = function(config){
38541     
38542     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
38543     
38544     Roo.bootstrap.MasonryBrick.register(this);
38545     
38546     this.addEvents({
38547         // raw events
38548         /**
38549          * @event click
38550          * When a MasonryBrick is clcik
38551          * @param {Roo.bootstrap.MasonryBrick} this
38552          * @param {Roo.EventObject} e
38553          */
38554         "click" : true
38555     });
38556 };
38557
38558 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
38559     
38560     /**
38561      * @cfg {String} title
38562      */   
38563     title : '',
38564     /**
38565      * @cfg {String} html
38566      */   
38567     html : '',
38568     /**
38569      * @cfg {String} bgimage
38570      */   
38571     bgimage : '',
38572     /**
38573      * @cfg {String} videourl
38574      */   
38575     videourl : '',
38576     /**
38577      * @cfg {String} cls
38578      */   
38579     cls : '',
38580     /**
38581      * @cfg {String} href
38582      */   
38583     href : '',
38584     /**
38585      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
38586      */   
38587     size : 'xs',
38588     
38589     /**
38590      * @cfg {String} placetitle (center|bottom)
38591      */   
38592     placetitle : '',
38593     
38594     /**
38595      * @cfg {Boolean} isFitContainer defalut true
38596      */   
38597     isFitContainer : true, 
38598     
38599     /**
38600      * @cfg {Boolean} preventDefault defalut false
38601      */   
38602     preventDefault : false, 
38603     
38604     /**
38605      * @cfg {Boolean} inverse defalut false
38606      */   
38607     maskInverse : false, 
38608     
38609     getAutoCreate : function()
38610     {
38611         if(!this.isFitContainer){
38612             return this.getSplitAutoCreate();
38613         }
38614         
38615         var cls = 'masonry-brick masonry-brick-full';
38616         
38617         if(this.href.length){
38618             cls += ' masonry-brick-link';
38619         }
38620         
38621         if(this.bgimage.length){
38622             cls += ' masonry-brick-image';
38623         }
38624         
38625         if(this.maskInverse){
38626             cls += ' mask-inverse';
38627         }
38628         
38629         if(!this.html.length && !this.maskInverse && !this.videourl.length){
38630             cls += ' enable-mask';
38631         }
38632         
38633         if(this.size){
38634             cls += ' masonry-' + this.size + '-brick';
38635         }
38636         
38637         if(this.placetitle.length){
38638             
38639             switch (this.placetitle) {
38640                 case 'center' :
38641                     cls += ' masonry-center-title';
38642                     break;
38643                 case 'bottom' :
38644                     cls += ' masonry-bottom-title';
38645                     break;
38646                 default:
38647                     break;
38648             }
38649             
38650         } else {
38651             if(!this.html.length && !this.bgimage.length){
38652                 cls += ' masonry-center-title';
38653             }
38654
38655             if(!this.html.length && this.bgimage.length){
38656                 cls += ' masonry-bottom-title';
38657             }
38658         }
38659         
38660         if(this.cls){
38661             cls += ' ' + this.cls;
38662         }
38663         
38664         var cfg = {
38665             tag: (this.href.length) ? 'a' : 'div',
38666             cls: cls,
38667             cn: [
38668                 {
38669                     tag: 'div',
38670                     cls: 'masonry-brick-mask'
38671                 },
38672                 {
38673                     tag: 'div',
38674                     cls: 'masonry-brick-paragraph',
38675                     cn: []
38676                 }
38677             ]
38678         };
38679         
38680         if(this.href.length){
38681             cfg.href = this.href;
38682         }
38683         
38684         var cn = cfg.cn[1].cn;
38685         
38686         if(this.title.length){
38687             cn.push({
38688                 tag: 'h4',
38689                 cls: 'masonry-brick-title',
38690                 html: this.title
38691             });
38692         }
38693         
38694         if(this.html.length){
38695             cn.push({
38696                 tag: 'p',
38697                 cls: 'masonry-brick-text',
38698                 html: this.html
38699             });
38700         }
38701         
38702         if (!this.title.length && !this.html.length) {
38703             cfg.cn[1].cls += ' hide';
38704         }
38705         
38706         if(this.bgimage.length){
38707             cfg.cn.push({
38708                 tag: 'img',
38709                 cls: 'masonry-brick-image-view',
38710                 src: this.bgimage
38711             });
38712         }
38713         
38714         if(this.videourl.length){
38715             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
38716             // youtube support only?
38717             cfg.cn.push({
38718                 tag: 'iframe',
38719                 cls: 'masonry-brick-image-view',
38720                 src: vurl,
38721                 frameborder : 0,
38722                 allowfullscreen : true
38723             });
38724         }
38725         
38726         return cfg;
38727         
38728     },
38729     
38730     getSplitAutoCreate : function()
38731     {
38732         var cls = 'masonry-brick masonry-brick-split';
38733         
38734         if(this.href.length){
38735             cls += ' masonry-brick-link';
38736         }
38737         
38738         if(this.bgimage.length){
38739             cls += ' masonry-brick-image';
38740         }
38741         
38742         if(this.size){
38743             cls += ' masonry-' + this.size + '-brick';
38744         }
38745         
38746         switch (this.placetitle) {
38747             case 'center' :
38748                 cls += ' masonry-center-title';
38749                 break;
38750             case 'bottom' :
38751                 cls += ' masonry-bottom-title';
38752                 break;
38753             default:
38754                 if(!this.bgimage.length){
38755                     cls += ' masonry-center-title';
38756                 }
38757
38758                 if(this.bgimage.length){
38759                     cls += ' masonry-bottom-title';
38760                 }
38761                 break;
38762         }
38763         
38764         if(this.cls){
38765             cls += ' ' + this.cls;
38766         }
38767         
38768         var cfg = {
38769             tag: (this.href.length) ? 'a' : 'div',
38770             cls: cls,
38771             cn: [
38772                 {
38773                     tag: 'div',
38774                     cls: 'masonry-brick-split-head',
38775                     cn: [
38776                         {
38777                             tag: 'div',
38778                             cls: 'masonry-brick-paragraph',
38779                             cn: []
38780                         }
38781                     ]
38782                 },
38783                 {
38784                     tag: 'div',
38785                     cls: 'masonry-brick-split-body',
38786                     cn: []
38787                 }
38788             ]
38789         };
38790         
38791         if(this.href.length){
38792             cfg.href = this.href;
38793         }
38794         
38795         if(this.title.length){
38796             cfg.cn[0].cn[0].cn.push({
38797                 tag: 'h4',
38798                 cls: 'masonry-brick-title',
38799                 html: this.title
38800             });
38801         }
38802         
38803         if(this.html.length){
38804             cfg.cn[1].cn.push({
38805                 tag: 'p',
38806                 cls: 'masonry-brick-text',
38807                 html: this.html
38808             });
38809         }
38810
38811         if(this.bgimage.length){
38812             cfg.cn[0].cn.push({
38813                 tag: 'img',
38814                 cls: 'masonry-brick-image-view',
38815                 src: this.bgimage
38816             });
38817         }
38818         
38819         if(this.videourl.length){
38820             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
38821             // youtube support only?
38822             cfg.cn[0].cn.cn.push({
38823                 tag: 'iframe',
38824                 cls: 'masonry-brick-image-view',
38825                 src: vurl,
38826                 frameborder : 0,
38827                 allowfullscreen : true
38828             });
38829         }
38830         
38831         return cfg;
38832     },
38833     
38834     initEvents: function() 
38835     {
38836         switch (this.size) {
38837             case 'xs' :
38838                 this.x = 1;
38839                 this.y = 1;
38840                 break;
38841             case 'sm' :
38842                 this.x = 2;
38843                 this.y = 2;
38844                 break;
38845             case 'md' :
38846             case 'md-left' :
38847             case 'md-right' :
38848                 this.x = 3;
38849                 this.y = 3;
38850                 break;
38851             case 'tall' :
38852                 this.x = 2;
38853                 this.y = 3;
38854                 break;
38855             case 'wide' :
38856                 this.x = 3;
38857                 this.y = 2;
38858                 break;
38859             case 'wide-thin' :
38860                 this.x = 3;
38861                 this.y = 1;
38862                 break;
38863                         
38864             default :
38865                 break;
38866         }
38867         
38868         if(Roo.isTouch){
38869             this.el.on('touchstart', this.onTouchStart, this);
38870             this.el.on('touchmove', this.onTouchMove, this);
38871             this.el.on('touchend', this.onTouchEnd, this);
38872             this.el.on('contextmenu', this.onContextMenu, this);
38873         } else {
38874             this.el.on('mouseenter'  ,this.enter, this);
38875             this.el.on('mouseleave', this.leave, this);
38876             this.el.on('click', this.onClick, this);
38877         }
38878         
38879         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
38880             this.parent().bricks.push(this);   
38881         }
38882         
38883     },
38884     
38885     onClick: function(e, el)
38886     {
38887         var time = this.endTimer - this.startTimer;
38888         // Roo.log(e.preventDefault());
38889         if(Roo.isTouch){
38890             if(time > 1000){
38891                 e.preventDefault();
38892                 return;
38893             }
38894         }
38895         
38896         if(!this.preventDefault){
38897             return;
38898         }
38899         
38900         e.preventDefault();
38901         
38902         if (this.activeClass != '') {
38903             this.selectBrick();
38904         }
38905         
38906         this.fireEvent('click', this, e);
38907     },
38908     
38909     enter: function(e, el)
38910     {
38911         e.preventDefault();
38912         
38913         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
38914             return;
38915         }
38916         
38917         if(this.bgimage.length && this.html.length){
38918             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
38919         }
38920     },
38921     
38922     leave: function(e, el)
38923     {
38924         e.preventDefault();
38925         
38926         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
38927             return;
38928         }
38929         
38930         if(this.bgimage.length && this.html.length){
38931             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
38932         }
38933     },
38934     
38935     onTouchStart: function(e, el)
38936     {
38937 //        e.preventDefault();
38938         
38939         this.touchmoved = false;
38940         
38941         if(!this.isFitContainer){
38942             return;
38943         }
38944         
38945         if(!this.bgimage.length || !this.html.length){
38946             return;
38947         }
38948         
38949         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
38950         
38951         this.timer = new Date().getTime();
38952         
38953     },
38954     
38955     onTouchMove: function(e, el)
38956     {
38957         this.touchmoved = true;
38958     },
38959     
38960     onContextMenu : function(e,el)
38961     {
38962         e.preventDefault();
38963         e.stopPropagation();
38964         return false;
38965     },
38966     
38967     onTouchEnd: function(e, el)
38968     {
38969 //        e.preventDefault();
38970         
38971         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
38972         
38973             this.leave(e,el);
38974             
38975             return;
38976         }
38977         
38978         if(!this.bgimage.length || !this.html.length){
38979             
38980             if(this.href.length){
38981                 window.location.href = this.href;
38982             }
38983             
38984             return;
38985         }
38986         
38987         if(!this.isFitContainer){
38988             return;
38989         }
38990         
38991         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
38992         
38993         window.location.href = this.href;
38994     },
38995     
38996     //selection on single brick only
38997     selectBrick : function() {
38998         
38999         if (!this.parentId) {
39000             return;
39001         }
39002         
39003         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
39004         var index = m.selectedBrick.indexOf(this.id);
39005         
39006         if ( index > -1) {
39007             m.selectedBrick.splice(index,1);
39008             this.el.removeClass(this.activeClass);
39009             return;
39010         }
39011         
39012         for(var i = 0; i < m.selectedBrick.length; i++) {
39013             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
39014             b.el.removeClass(b.activeClass);
39015         }
39016         
39017         m.selectedBrick = [];
39018         
39019         m.selectedBrick.push(this.id);
39020         this.el.addClass(this.activeClass);
39021         return;
39022     },
39023     
39024     isSelected : function(){
39025         return this.el.hasClass(this.activeClass);
39026         
39027     }
39028 });
39029
39030 Roo.apply(Roo.bootstrap.MasonryBrick, {
39031     
39032     //groups: {},
39033     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
39034      /**
39035     * register a Masonry Brick
39036     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39037     */
39038     
39039     register : function(brick)
39040     {
39041         //this.groups[brick.id] = brick;
39042         this.groups.add(brick.id, brick);
39043     },
39044     /**
39045     * fetch a  masonry brick based on the masonry brick ID
39046     * @param {string} the masonry brick to add
39047     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
39048     */
39049     
39050     get: function(brick_id) 
39051     {
39052         // if (typeof(this.groups[brick_id]) == 'undefined') {
39053         //     return false;
39054         // }
39055         // return this.groups[brick_id] ;
39056         
39057         if(this.groups.key(brick_id)) {
39058             return this.groups.key(brick_id);
39059         }
39060         
39061         return false;
39062     }
39063     
39064     
39065     
39066 });
39067
39068  /*
39069  * - LGPL
39070  *
39071  * element
39072  * 
39073  */
39074
39075 /**
39076  * @class Roo.bootstrap.Brick
39077  * @extends Roo.bootstrap.Component
39078  * Bootstrap Brick class
39079  * 
39080  * @constructor
39081  * Create a new Brick
39082  * @param {Object} config The config object
39083  */
39084
39085 Roo.bootstrap.Brick = function(config){
39086     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
39087     
39088     this.addEvents({
39089         // raw events
39090         /**
39091          * @event click
39092          * When a Brick is click
39093          * @param {Roo.bootstrap.Brick} this
39094          * @param {Roo.EventObject} e
39095          */
39096         "click" : true
39097     });
39098 };
39099
39100 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
39101     
39102     /**
39103      * @cfg {String} title
39104      */   
39105     title : '',
39106     /**
39107      * @cfg {String} html
39108      */   
39109     html : '',
39110     /**
39111      * @cfg {String} bgimage
39112      */   
39113     bgimage : '',
39114     /**
39115      * @cfg {String} cls
39116      */   
39117     cls : '',
39118     /**
39119      * @cfg {String} href
39120      */   
39121     href : '',
39122     /**
39123      * @cfg {String} video
39124      */   
39125     video : '',
39126     /**
39127      * @cfg {Boolean} square
39128      */   
39129     square : true,
39130     
39131     getAutoCreate : function()
39132     {
39133         var cls = 'roo-brick';
39134         
39135         if(this.href.length){
39136             cls += ' roo-brick-link';
39137         }
39138         
39139         if(this.bgimage.length){
39140             cls += ' roo-brick-image';
39141         }
39142         
39143         if(!this.html.length && !this.bgimage.length){
39144             cls += ' roo-brick-center-title';
39145         }
39146         
39147         if(!this.html.length && this.bgimage.length){
39148             cls += ' roo-brick-bottom-title';
39149         }
39150         
39151         if(this.cls){
39152             cls += ' ' + this.cls;
39153         }
39154         
39155         var cfg = {
39156             tag: (this.href.length) ? 'a' : 'div',
39157             cls: cls,
39158             cn: [
39159                 {
39160                     tag: 'div',
39161                     cls: 'roo-brick-paragraph',
39162                     cn: []
39163                 }
39164             ]
39165         };
39166         
39167         if(this.href.length){
39168             cfg.href = this.href;
39169         }
39170         
39171         var cn = cfg.cn[0].cn;
39172         
39173         if(this.title.length){
39174             cn.push({
39175                 tag: 'h4',
39176                 cls: 'roo-brick-title',
39177                 html: this.title
39178             });
39179         }
39180         
39181         if(this.html.length){
39182             cn.push({
39183                 tag: 'p',
39184                 cls: 'roo-brick-text',
39185                 html: this.html
39186             });
39187         } else {
39188             cn.cls += ' hide';
39189         }
39190         
39191         if(this.bgimage.length){
39192             cfg.cn.push({
39193                 tag: 'img',
39194                 cls: 'roo-brick-image-view',
39195                 src: this.bgimage
39196             });
39197         }
39198         
39199         return cfg;
39200     },
39201     
39202     initEvents: function() 
39203     {
39204         if(this.title.length || this.html.length){
39205             this.el.on('mouseenter'  ,this.enter, this);
39206             this.el.on('mouseleave', this.leave, this);
39207         }
39208         
39209         Roo.EventManager.onWindowResize(this.resize, this); 
39210         
39211         if(this.bgimage.length){
39212             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
39213             this.imageEl.on('load', this.onImageLoad, this);
39214             return;
39215         }
39216         
39217         this.resize();
39218     },
39219     
39220     onImageLoad : function()
39221     {
39222         this.resize();
39223     },
39224     
39225     resize : function()
39226     {
39227         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
39228         
39229         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
39230         
39231         if(this.bgimage.length){
39232             var image = this.el.select('.roo-brick-image-view', true).first();
39233             
39234             image.setWidth(paragraph.getWidth());
39235             
39236             if(this.square){
39237                 image.setHeight(paragraph.getWidth());
39238             }
39239             
39240             this.el.setHeight(image.getHeight());
39241             paragraph.setHeight(image.getHeight());
39242             
39243         }
39244         
39245     },
39246     
39247     enter: function(e, el)
39248     {
39249         e.preventDefault();
39250         
39251         if(this.bgimage.length){
39252             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
39253             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
39254         }
39255     },
39256     
39257     leave: function(e, el)
39258     {
39259         e.preventDefault();
39260         
39261         if(this.bgimage.length){
39262             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
39263             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
39264         }
39265     }
39266     
39267 });
39268
39269  
39270
39271  /*
39272  * - LGPL
39273  *
39274  * Number field 
39275  */
39276
39277 /**
39278  * @class Roo.bootstrap.form.NumberField
39279  * @extends Roo.bootstrap.form.Input
39280  * Bootstrap NumberField class
39281  * 
39282  * 
39283  * 
39284  * 
39285  * @constructor
39286  * Create a new NumberField
39287  * @param {Object} config The config object
39288  */
39289
39290 Roo.bootstrap.form.NumberField = function(config){
39291     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
39292 };
39293
39294 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
39295     
39296     /**
39297      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39298      */
39299     allowDecimals : true,
39300     /**
39301      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39302      */
39303     decimalSeparator : ".",
39304     /**
39305      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39306      */
39307     decimalPrecision : 2,
39308     /**
39309      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39310      */
39311     allowNegative : true,
39312     
39313     /**
39314      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
39315      */
39316     allowZero: true,
39317     /**
39318      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39319      */
39320     minValue : Number.NEGATIVE_INFINITY,
39321     /**
39322      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39323      */
39324     maxValue : Number.MAX_VALUE,
39325     /**
39326      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39327      */
39328     minText : "The minimum value for this field is {0}",
39329     /**
39330      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39331      */
39332     maxText : "The maximum value for this field is {0}",
39333     /**
39334      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39335      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39336      */
39337     nanText : "{0} is not a valid number",
39338     /**
39339      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
39340      */
39341     thousandsDelimiter : false,
39342     /**
39343      * @cfg {String} valueAlign alignment of value
39344      */
39345     valueAlign : "left",
39346
39347     getAutoCreate : function()
39348     {
39349         var hiddenInput = {
39350             tag: 'input',
39351             type: 'hidden',
39352             id: Roo.id(),
39353             cls: 'hidden-number-input'
39354         };
39355         
39356         if (this.name) {
39357             hiddenInput.name = this.name;
39358         }
39359         
39360         this.name = '';
39361         
39362         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
39363         
39364         this.name = hiddenInput.name;
39365         
39366         if(cfg.cn.length > 0) {
39367             cfg.cn.push(hiddenInput);
39368         }
39369         
39370         return cfg;
39371     },
39372
39373     // private
39374     initEvents : function()
39375     {   
39376         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
39377         
39378         var allowed = "0123456789";
39379         
39380         if(this.allowDecimals){
39381             allowed += this.decimalSeparator;
39382         }
39383         
39384         if(this.allowNegative){
39385             allowed += "-";
39386         }
39387         
39388         if(this.thousandsDelimiter) {
39389             allowed += ",";
39390         }
39391         
39392         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39393         
39394         var keyPress = function(e){
39395             
39396             var k = e.getKey();
39397             
39398             var c = e.getCharCode();
39399             
39400             if(
39401                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39402                     allowed.indexOf(String.fromCharCode(c)) === -1
39403             ){
39404                 e.stopEvent();
39405                 return;
39406             }
39407             
39408             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39409                 return;
39410             }
39411             
39412             if(allowed.indexOf(String.fromCharCode(c)) === -1){
39413                 e.stopEvent();
39414             }
39415         };
39416         
39417         this.el.on("keypress", keyPress, this);
39418     },
39419     
39420     validateValue : function(value)
39421     {
39422         
39423         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
39424             return false;
39425         }
39426         
39427         var num = this.parseValue(value);
39428         
39429         if(isNaN(num)){
39430             this.markInvalid(String.format(this.nanText, value));
39431             return false;
39432         }
39433         
39434         if(num < this.minValue){
39435             this.markInvalid(String.format(this.minText, this.minValue));
39436             return false;
39437         }
39438         
39439         if(num > this.maxValue){
39440             this.markInvalid(String.format(this.maxText, this.maxValue));
39441             return false;
39442         }
39443         
39444         return true;
39445     },
39446
39447     getValue : function()
39448     {
39449         var v = this.hiddenEl().getValue();
39450         
39451         return this.fixPrecision(this.parseValue(v));
39452     },
39453
39454     parseValue : function(value)
39455     {
39456         if(this.thousandsDelimiter) {
39457             value += "";
39458             r = new RegExp(",", "g");
39459             value = value.replace(r, "");
39460         }
39461         
39462         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
39463         return isNaN(value) ? '' : value;
39464     },
39465
39466     fixPrecision : function(value)
39467     {
39468         if(this.thousandsDelimiter) {
39469             value += "";
39470             r = new RegExp(",", "g");
39471             value = value.replace(r, "");
39472         }
39473         
39474         var nan = isNaN(value);
39475         
39476         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
39477             return nan ? '' : value;
39478         }
39479         return parseFloat(value).toFixed(this.decimalPrecision);
39480     },
39481
39482     setValue : function(v)
39483     {
39484         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
39485         
39486         this.value = v;
39487         
39488         if(this.rendered){
39489             
39490             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39491             
39492             this.inputEl().dom.value = (v == '') ? '' :
39493                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
39494             
39495             if(!this.allowZero && v === '0') {
39496                 this.hiddenEl().dom.value = '';
39497                 this.inputEl().dom.value = '';
39498             }
39499             
39500             this.validate();
39501         }
39502     },
39503
39504     decimalPrecisionFcn : function(v)
39505     {
39506         return Math.floor(v);
39507     },
39508
39509     beforeBlur : function()
39510     {
39511         var v = this.parseValue(this.getRawValue());
39512         
39513         if(v || v === 0 || v === ''){
39514             this.setValue(v);
39515         }
39516     },
39517     
39518     hiddenEl : function()
39519     {
39520         return this.el.select('input.hidden-number-input',true).first();
39521     }
39522     
39523 });
39524
39525  
39526
39527 /*
39528 * Licence: LGPL
39529 */
39530
39531 /**
39532  * @class Roo.bootstrap.DocumentSlider
39533  * @extends Roo.bootstrap.Component
39534  * Bootstrap DocumentSlider class
39535  * 
39536  * @constructor
39537  * Create a new DocumentViewer
39538  * @param {Object} config The config object
39539  */
39540
39541 Roo.bootstrap.DocumentSlider = function(config){
39542     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
39543     
39544     this.files = [];
39545     
39546     this.addEvents({
39547         /**
39548          * @event initial
39549          * Fire after initEvent
39550          * @param {Roo.bootstrap.DocumentSlider} this
39551          */
39552         "initial" : true,
39553         /**
39554          * @event update
39555          * Fire after update
39556          * @param {Roo.bootstrap.DocumentSlider} this
39557          */
39558         "update" : true,
39559         /**
39560          * @event click
39561          * Fire after click
39562          * @param {Roo.bootstrap.DocumentSlider} this
39563          */
39564         "click" : true
39565     });
39566 };
39567
39568 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
39569     
39570     files : false,
39571     
39572     indicator : 0,
39573     
39574     getAutoCreate : function()
39575     {
39576         var cfg = {
39577             tag : 'div',
39578             cls : 'roo-document-slider',
39579             cn : [
39580                 {
39581                     tag : 'div',
39582                     cls : 'roo-document-slider-header',
39583                     cn : [
39584                         {
39585                             tag : 'div',
39586                             cls : 'roo-document-slider-header-title'
39587                         }
39588                     ]
39589                 },
39590                 {
39591                     tag : 'div',
39592                     cls : 'roo-document-slider-body',
39593                     cn : [
39594                         {
39595                             tag : 'div',
39596                             cls : 'roo-document-slider-prev',
39597                             cn : [
39598                                 {
39599                                     tag : 'i',
39600                                     cls : 'fa fa-chevron-left'
39601                                 }
39602                             ]
39603                         },
39604                         {
39605                             tag : 'div',
39606                             cls : 'roo-document-slider-thumb',
39607                             cn : [
39608                                 {
39609                                     tag : 'img',
39610                                     cls : 'roo-document-slider-image'
39611                                 }
39612                             ]
39613                         },
39614                         {
39615                             tag : 'div',
39616                             cls : 'roo-document-slider-next',
39617                             cn : [
39618                                 {
39619                                     tag : 'i',
39620                                     cls : 'fa fa-chevron-right'
39621                                 }
39622                             ]
39623                         }
39624                     ]
39625                 }
39626             ]
39627         };
39628         
39629         return cfg;
39630     },
39631     
39632     initEvents : function()
39633     {
39634         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
39635         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
39636         
39637         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
39638         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
39639         
39640         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
39641         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
39642         
39643         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
39644         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
39645         
39646         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
39647         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
39648         
39649         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
39650         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39651         
39652         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
39653         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39654         
39655         this.thumbEl.on('click', this.onClick, this);
39656         
39657         this.prevIndicator.on('click', this.prev, this);
39658         
39659         this.nextIndicator.on('click', this.next, this);
39660         
39661     },
39662     
39663     initial : function()
39664     {
39665         if(this.files.length){
39666             this.indicator = 1;
39667             this.update()
39668         }
39669         
39670         this.fireEvent('initial', this);
39671     },
39672     
39673     update : function()
39674     {
39675         this.imageEl.attr('src', this.files[this.indicator - 1]);
39676         
39677         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
39678         
39679         this.prevIndicator.show();
39680         
39681         if(this.indicator == 1){
39682             this.prevIndicator.hide();
39683         }
39684         
39685         this.nextIndicator.show();
39686         
39687         if(this.indicator == this.files.length){
39688             this.nextIndicator.hide();
39689         }
39690         
39691         this.thumbEl.scrollTo('top');
39692         
39693         this.fireEvent('update', this);
39694     },
39695     
39696     onClick : function(e)
39697     {
39698         e.preventDefault();
39699         
39700         this.fireEvent('click', this);
39701     },
39702     
39703     prev : function(e)
39704     {
39705         e.preventDefault();
39706         
39707         this.indicator = Math.max(1, this.indicator - 1);
39708         
39709         this.update();
39710     },
39711     
39712     next : function(e)
39713     {
39714         e.preventDefault();
39715         
39716         this.indicator = Math.min(this.files.length, this.indicator + 1);
39717         
39718         this.update();
39719     }
39720 });
39721 /*
39722  * - LGPL
39723  *
39724  * RadioSet
39725  *
39726  *
39727  */
39728
39729 /**
39730  * @class Roo.bootstrap.form.RadioSet
39731  * @extends Roo.bootstrap.form.Input
39732  * @children Roo.bootstrap.form.Radio
39733  * Bootstrap RadioSet class
39734  * @cfg {String} indicatorpos (left|right) default left
39735  * @cfg {Boolean} inline (true|false) inline the element (default true)
39736  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
39737  * @constructor
39738  * Create a new RadioSet
39739  * @param {Object} config The config object
39740  */
39741
39742 Roo.bootstrap.form.RadioSet = function(config){
39743     
39744     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
39745     
39746     this.radioes = [];
39747     
39748     Roo.bootstrap.form.RadioSet.register(this);
39749     
39750     this.addEvents({
39751         /**
39752         * @event check
39753         * Fires when the element is checked or unchecked.
39754         * @param {Roo.bootstrap.form.RadioSet} this This radio
39755         * @param {Roo.bootstrap.form.Radio} item The checked item
39756         */
39757        check : true,
39758        /**
39759         * @event click
39760         * Fires when the element is click.
39761         * @param {Roo.bootstrap.form.RadioSet} this This radio set
39762         * @param {Roo.bootstrap.form.Radio} item The checked item
39763         * @param {Roo.EventObject} e The event object
39764         */
39765        click : true
39766     });
39767     
39768 };
39769
39770 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
39771
39772     radioes : false,
39773     
39774     inline : true,
39775     
39776     weight : '',
39777     
39778     indicatorpos : 'left',
39779     
39780     getAutoCreate : function()
39781     {
39782         var label = {
39783             tag : 'label',
39784             cls : 'roo-radio-set-label',
39785             cn : [
39786                 {
39787                     tag : 'span',
39788                     html : this.fieldLabel
39789                 }
39790             ]
39791         };
39792         if (Roo.bootstrap.version == 3) {
39793             
39794             
39795             if(this.indicatorpos == 'left'){
39796                 label.cn.unshift({
39797                     tag : 'i',
39798                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
39799                     tooltip : 'This field is required'
39800                 });
39801             } else {
39802                 label.cn.push({
39803                     tag : 'i',
39804                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
39805                     tooltip : 'This field is required'
39806                 });
39807             }
39808         }
39809         var items = {
39810             tag : 'div',
39811             cls : 'roo-radio-set-items'
39812         };
39813         
39814         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
39815         
39816         if (align === 'left' && this.fieldLabel.length) {
39817             
39818             items = {
39819                 cls : "roo-radio-set-right", 
39820                 cn: [
39821                     items
39822                 ]
39823             };
39824             
39825             if(this.labelWidth > 12){
39826                 label.style = "width: " + this.labelWidth + 'px';
39827             }
39828             
39829             if(this.labelWidth < 13 && this.labelmd == 0){
39830                 this.labelmd = this.labelWidth;
39831             }
39832             
39833             if(this.labellg > 0){
39834                 label.cls += ' col-lg-' + this.labellg;
39835                 items.cls += ' col-lg-' + (12 - this.labellg);
39836             }
39837             
39838             if(this.labelmd > 0){
39839                 label.cls += ' col-md-' + this.labelmd;
39840                 items.cls += ' col-md-' + (12 - this.labelmd);
39841             }
39842             
39843             if(this.labelsm > 0){
39844                 label.cls += ' col-sm-' + this.labelsm;
39845                 items.cls += ' col-sm-' + (12 - this.labelsm);
39846             }
39847             
39848             if(this.labelxs > 0){
39849                 label.cls += ' col-xs-' + this.labelxs;
39850                 items.cls += ' col-xs-' + (12 - this.labelxs);
39851             }
39852         }
39853         
39854         var cfg = {
39855             tag : 'div',
39856             cls : 'roo-radio-set',
39857             cn : [
39858                 {
39859                     tag : 'input',
39860                     cls : 'roo-radio-set-input',
39861                     type : 'hidden',
39862                     name : this.name,
39863                     value : this.value ? this.value :  ''
39864                 },
39865                 label,
39866                 items
39867             ]
39868         };
39869         
39870         if(this.weight.length){
39871             cfg.cls += ' roo-radio-' + this.weight;
39872         }
39873         
39874         if(this.inline) {
39875             cfg.cls += ' roo-radio-set-inline';
39876         }
39877         
39878         var settings=this;
39879         ['xs','sm','md','lg'].map(function(size){
39880             if (settings[size]) {
39881                 cfg.cls += ' col-' + size + '-' + settings[size];
39882             }
39883         });
39884         
39885         return cfg;
39886         
39887     },
39888
39889     initEvents : function()
39890     {
39891         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
39892         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
39893         
39894         if(!this.fieldLabel.length){
39895             this.labelEl.hide();
39896         }
39897         
39898         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
39899         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
39900         
39901         this.indicator = this.indicatorEl();
39902         
39903         if(this.indicator){
39904             this.indicator.addClass('invisible');
39905         }
39906         
39907         this.originalValue = this.getValue();
39908         
39909     },
39910     
39911     inputEl: function ()
39912     {
39913         return this.el.select('.roo-radio-set-input', true).first();
39914     },
39915     
39916     getChildContainer : function()
39917     {
39918         return this.itemsEl;
39919     },
39920     
39921     register : function(item)
39922     {
39923         this.radioes.push(item);
39924         
39925     },
39926     
39927     validate : function()
39928     {   
39929         if(this.getVisibilityEl().hasClass('hidden')){
39930             return true;
39931         }
39932         
39933         var valid = false;
39934         
39935         Roo.each(this.radioes, function(i){
39936             if(!i.checked){
39937                 return;
39938             }
39939             
39940             valid = true;
39941             return false;
39942         });
39943         
39944         if(this.allowBlank) {
39945             return true;
39946         }
39947         
39948         if(this.disabled || valid){
39949             this.markValid();
39950             return true;
39951         }
39952         
39953         this.markInvalid();
39954         return false;
39955         
39956     },
39957     
39958     markValid : function()
39959     {
39960         if(this.labelEl.isVisible(true) && this.indicatorEl()){
39961             this.indicatorEl().removeClass('visible');
39962             this.indicatorEl().addClass('invisible');
39963         }
39964         
39965         
39966         if (Roo.bootstrap.version == 3) {
39967             this.el.removeClass([this.invalidClass, this.validClass]);
39968             this.el.addClass(this.validClass);
39969         } else {
39970             this.el.removeClass(['is-invalid','is-valid']);
39971             this.el.addClass(['is-valid']);
39972         }
39973         this.fireEvent('valid', this);
39974     },
39975     
39976     markInvalid : function(msg)
39977     {
39978         if(this.allowBlank || this.disabled){
39979             return;
39980         }
39981         
39982         if(this.labelEl.isVisible(true) && this.indicatorEl()){
39983             this.indicatorEl().removeClass('invisible');
39984             this.indicatorEl().addClass('visible');
39985         }
39986         if (Roo.bootstrap.version == 3) {
39987             this.el.removeClass([this.invalidClass, this.validClass]);
39988             this.el.addClass(this.invalidClass);
39989         } else {
39990             this.el.removeClass(['is-invalid','is-valid']);
39991             this.el.addClass(['is-invalid']);
39992         }
39993         
39994         this.fireEvent('invalid', this, msg);
39995         
39996     },
39997     
39998     setValue : function(v, suppressEvent)
39999     {   
40000         if(this.value === v){
40001             return;
40002         }
40003         
40004         this.value = v;
40005         
40006         if(this.rendered){
40007             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40008         }
40009         
40010         Roo.each(this.radioes, function(i){
40011             i.checked = false;
40012             i.el.removeClass('checked');
40013         });
40014         
40015         Roo.each(this.radioes, function(i){
40016             
40017             if(i.value === v || i.value.toString() === v.toString()){
40018                 i.checked = true;
40019                 i.el.addClass('checked');
40020                 
40021                 if(suppressEvent !== true){
40022                     this.fireEvent('check', this, i);
40023                 }
40024                 
40025                 return false;
40026             }
40027             
40028         }, this);
40029         
40030         this.validate();
40031     },
40032     
40033     clearInvalid : function(){
40034         
40035         if(!this.el || this.preventMark){
40036             return;
40037         }
40038         
40039         this.el.removeClass([this.invalidClass]);
40040         
40041         this.fireEvent('valid', this);
40042     }
40043     
40044 });
40045
40046 Roo.apply(Roo.bootstrap.form.RadioSet, {
40047     
40048     groups: {},
40049     
40050     register : function(set)
40051     {
40052         this.groups[set.name] = set;
40053     },
40054     
40055     get: function(name) 
40056     {
40057         if (typeof(this.groups[name]) == 'undefined') {
40058             return false;
40059         }
40060         
40061         return this.groups[name] ;
40062     }
40063     
40064 });
40065 /*
40066  * Based on:
40067  * Ext JS Library 1.1.1
40068  * Copyright(c) 2006-2007, Ext JS, LLC.
40069  *
40070  * Originally Released Under LGPL - original licence link has changed is not relivant.
40071  *
40072  * Fork - LGPL
40073  * <script type="text/javascript">
40074  */
40075
40076
40077 /**
40078  * @class Roo.bootstrap.SplitBar
40079  * @extends Roo.util.Observable
40080  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
40081  * <br><br>
40082  * Usage:
40083  * <pre><code>
40084 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
40085                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
40086 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
40087 split.minSize = 100;
40088 split.maxSize = 600;
40089 split.animate = true;
40090 split.on('moved', splitterMoved);
40091 </code></pre>
40092  * @constructor
40093  * Create a new SplitBar
40094  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
40095  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
40096  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40097  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
40098                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
40099                         position of the SplitBar).
40100  */
40101 Roo.bootstrap.SplitBar = function(cfg){
40102     
40103     /** @private */
40104     
40105     //{
40106     //  dragElement : elm
40107     //  resizingElement: el,
40108         // optional..
40109     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
40110     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
40111         // existingProxy ???
40112     //}
40113     
40114     this.el = Roo.get(cfg.dragElement, true);
40115     this.el.dom.unselectable = "on";
40116     /** @private */
40117     this.resizingEl = Roo.get(cfg.resizingElement, true);
40118
40119     /**
40120      * @private
40121      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40122      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
40123      * @type Number
40124      */
40125     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
40126     
40127     /**
40128      * The minimum size of the resizing element. (Defaults to 0)
40129      * @type Number
40130      */
40131     this.minSize = 0;
40132     
40133     /**
40134      * The maximum size of the resizing element. (Defaults to 2000)
40135      * @type Number
40136      */
40137     this.maxSize = 2000;
40138     
40139     /**
40140      * Whether to animate the transition to the new size
40141      * @type Boolean
40142      */
40143     this.animate = false;
40144     
40145     /**
40146      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
40147      * @type Boolean
40148      */
40149     this.useShim = false;
40150     
40151     /** @private */
40152     this.shim = null;
40153     
40154     if(!cfg.existingProxy){
40155         /** @private */
40156         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
40157     }else{
40158         this.proxy = Roo.get(cfg.existingProxy).dom;
40159     }
40160     /** @private */
40161     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
40162     
40163     /** @private */
40164     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
40165     
40166     /** @private */
40167     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
40168     
40169     /** @private */
40170     this.dragSpecs = {};
40171     
40172     /**
40173      * @private The adapter to use to positon and resize elements
40174      */
40175     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40176     this.adapter.init(this);
40177     
40178     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40179         /** @private */
40180         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
40181         this.el.addClass("roo-splitbar-h");
40182     }else{
40183         /** @private */
40184         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
40185         this.el.addClass("roo-splitbar-v");
40186     }
40187     
40188     this.addEvents({
40189         /**
40190          * @event resize
40191          * Fires when the splitter is moved (alias for {@link #event-moved})
40192          * @param {Roo.bootstrap.SplitBar} this
40193          * @param {Number} newSize the new width or height
40194          */
40195         "resize" : true,
40196         /**
40197          * @event moved
40198          * Fires when the splitter is moved
40199          * @param {Roo.bootstrap.SplitBar} this
40200          * @param {Number} newSize the new width or height
40201          */
40202         "moved" : true,
40203         /**
40204          * @event beforeresize
40205          * Fires before the splitter is dragged
40206          * @param {Roo.bootstrap.SplitBar} this
40207          */
40208         "beforeresize" : true,
40209
40210         "beforeapply" : true
40211     });
40212
40213     Roo.util.Observable.call(this);
40214 };
40215
40216 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
40217     onStartProxyDrag : function(x, y){
40218         this.fireEvent("beforeresize", this);
40219         if(!this.overlay){
40220             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
40221             o.unselectable();
40222             o.enableDisplayMode("block");
40223             // all splitbars share the same overlay
40224             Roo.bootstrap.SplitBar.prototype.overlay = o;
40225         }
40226         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
40227         this.overlay.show();
40228         Roo.get(this.proxy).setDisplayed("block");
40229         var size = this.adapter.getElementSize(this);
40230         this.activeMinSize = this.getMinimumSize();;
40231         this.activeMaxSize = this.getMaximumSize();;
40232         var c1 = size - this.activeMinSize;
40233         var c2 = Math.max(this.activeMaxSize - size, 0);
40234         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40235             this.dd.resetConstraints();
40236             this.dd.setXConstraint(
40237                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
40238                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
40239             );
40240             this.dd.setYConstraint(0, 0);
40241         }else{
40242             this.dd.resetConstraints();
40243             this.dd.setXConstraint(0, 0);
40244             this.dd.setYConstraint(
40245                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
40246                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
40247             );
40248          }
40249         this.dragSpecs.startSize = size;
40250         this.dragSpecs.startPoint = [x, y];
40251         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
40252     },
40253     
40254     /** 
40255      * @private Called after the drag operation by the DDProxy
40256      */
40257     onEndProxyDrag : function(e){
40258         Roo.get(this.proxy).setDisplayed(false);
40259         var endPoint = Roo.lib.Event.getXY(e);
40260         if(this.overlay){
40261             this.overlay.hide();
40262         }
40263         var newSize;
40264         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40265             newSize = this.dragSpecs.startSize + 
40266                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
40267                     endPoint[0] - this.dragSpecs.startPoint[0] :
40268                     this.dragSpecs.startPoint[0] - endPoint[0]
40269                 );
40270         }else{
40271             newSize = this.dragSpecs.startSize + 
40272                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
40273                     endPoint[1] - this.dragSpecs.startPoint[1] :
40274                     this.dragSpecs.startPoint[1] - endPoint[1]
40275                 );
40276         }
40277         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
40278         if(newSize != this.dragSpecs.startSize){
40279             if(this.fireEvent('beforeapply', this, newSize) !== false){
40280                 this.adapter.setElementSize(this, newSize);
40281                 this.fireEvent("moved", this, newSize);
40282                 this.fireEvent("resize", this, newSize);
40283             }
40284         }
40285     },
40286     
40287     /**
40288      * Get the adapter this SplitBar uses
40289      * @return The adapter object
40290      */
40291     getAdapter : function(){
40292         return this.adapter;
40293     },
40294     
40295     /**
40296      * Set the adapter this SplitBar uses
40297      * @param {Object} adapter A SplitBar adapter object
40298      */
40299     setAdapter : function(adapter){
40300         this.adapter = adapter;
40301         this.adapter.init(this);
40302     },
40303     
40304     /**
40305      * Gets the minimum size for the resizing element
40306      * @return {Number} The minimum size
40307      */
40308     getMinimumSize : function(){
40309         return this.minSize;
40310     },
40311     
40312     /**
40313      * Sets the minimum size for the resizing element
40314      * @param {Number} minSize The minimum size
40315      */
40316     setMinimumSize : function(minSize){
40317         this.minSize = minSize;
40318     },
40319     
40320     /**
40321      * Gets the maximum size for the resizing element
40322      * @return {Number} The maximum size
40323      */
40324     getMaximumSize : function(){
40325         return this.maxSize;
40326     },
40327     
40328     /**
40329      * Sets the maximum size for the resizing element
40330      * @param {Number} maxSize The maximum size
40331      */
40332     setMaximumSize : function(maxSize){
40333         this.maxSize = maxSize;
40334     },
40335     
40336     /**
40337      * Sets the initialize size for the resizing element
40338      * @param {Number} size The initial size
40339      */
40340     setCurrentSize : function(size){
40341         var oldAnimate = this.animate;
40342         this.animate = false;
40343         this.adapter.setElementSize(this, size);
40344         this.animate = oldAnimate;
40345     },
40346     
40347     /**
40348      * Destroy this splitbar. 
40349      * @param {Boolean} removeEl True to remove the element
40350      */
40351     destroy : function(removeEl){
40352         if(this.shim){
40353             this.shim.remove();
40354         }
40355         this.dd.unreg();
40356         this.proxy.parentNode.removeChild(this.proxy);
40357         if(removeEl){
40358             this.el.remove();
40359         }
40360     }
40361 });
40362
40363 /**
40364  * @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.
40365  */
40366 Roo.bootstrap.SplitBar.createProxy = function(dir){
40367     var proxy = new Roo.Element(document.createElement("div"));
40368     proxy.unselectable();
40369     var cls = 'roo-splitbar-proxy';
40370     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
40371     document.body.appendChild(proxy.dom);
40372     return proxy.dom;
40373 };
40374
40375 /** 
40376  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
40377  * Default Adapter. It assumes the splitter and resizing element are not positioned
40378  * elements and only gets/sets the width of the element. Generally used for table based layouts.
40379  */
40380 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
40381 };
40382
40383 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
40384     // do nothing for now
40385     init : function(s){
40386     
40387     },
40388     /**
40389      * Called before drag operations to get the current size of the resizing element. 
40390      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40391      */
40392      getElementSize : function(s){
40393         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40394             return s.resizingEl.getWidth();
40395         }else{
40396             return s.resizingEl.getHeight();
40397         }
40398     },
40399     
40400     /**
40401      * Called after drag operations to set the size of the resizing element.
40402      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40403      * @param {Number} newSize The new size to set
40404      * @param {Function} onComplete A function to be invoked when resizing is complete
40405      */
40406     setElementSize : function(s, newSize, onComplete){
40407         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40408             if(!s.animate){
40409                 s.resizingEl.setWidth(newSize);
40410                 if(onComplete){
40411                     onComplete(s, newSize);
40412                 }
40413             }else{
40414                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
40415             }
40416         }else{
40417             
40418             if(!s.animate){
40419                 s.resizingEl.setHeight(newSize);
40420                 if(onComplete){
40421                     onComplete(s, newSize);
40422                 }
40423             }else{
40424                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
40425             }
40426         }
40427     }
40428 };
40429
40430 /** 
40431  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
40432  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
40433  * Adapter that  moves the splitter element to align with the resized sizing element. 
40434  * Used with an absolute positioned SplitBar.
40435  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
40436  * document.body, make sure you assign an id to the body element.
40437  */
40438 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
40439     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40440     this.container = Roo.get(container);
40441 };
40442
40443 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
40444     init : function(s){
40445         this.basic.init(s);
40446     },
40447     
40448     getElementSize : function(s){
40449         return this.basic.getElementSize(s);
40450     },
40451     
40452     setElementSize : function(s, newSize, onComplete){
40453         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
40454     },
40455     
40456     moveSplitter : function(s){
40457         var yes = Roo.bootstrap.SplitBar;
40458         switch(s.placement){
40459             case yes.LEFT:
40460                 s.el.setX(s.resizingEl.getRight());
40461                 break;
40462             case yes.RIGHT:
40463                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
40464                 break;
40465             case yes.TOP:
40466                 s.el.setY(s.resizingEl.getBottom());
40467                 break;
40468             case yes.BOTTOM:
40469                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
40470                 break;
40471         }
40472     }
40473 };
40474
40475 /**
40476  * Orientation constant - Create a vertical SplitBar
40477  * @static
40478  * @type Number
40479  */
40480 Roo.bootstrap.SplitBar.VERTICAL = 1;
40481
40482 /**
40483  * Orientation constant - Create a horizontal SplitBar
40484  * @static
40485  * @type Number
40486  */
40487 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
40488
40489 /**
40490  * Placement constant - The resizing element is to the left of the splitter element
40491  * @static
40492  * @type Number
40493  */
40494 Roo.bootstrap.SplitBar.LEFT = 1;
40495
40496 /**
40497  * Placement constant - The resizing element is to the right of the splitter element
40498  * @static
40499  * @type Number
40500  */
40501 Roo.bootstrap.SplitBar.RIGHT = 2;
40502
40503 /**
40504  * Placement constant - The resizing element is positioned above the splitter element
40505  * @static
40506  * @type Number
40507  */
40508 Roo.bootstrap.SplitBar.TOP = 3;
40509
40510 /**
40511  * Placement constant - The resizing element is positioned under splitter element
40512  * @static
40513  * @type Number
40514  */
40515 Roo.bootstrap.SplitBar.BOTTOM = 4;
40516 /*
40517  * Based on:
40518  * Ext JS Library 1.1.1
40519  * Copyright(c) 2006-2007, Ext JS, LLC.
40520  *
40521  * Originally Released Under LGPL - original licence link has changed is not relivant.
40522  *
40523  * Fork - LGPL
40524  * <script type="text/javascript">
40525  */
40526
40527 /**
40528  * @class Roo.bootstrap.layout.Manager
40529  * @extends Roo.bootstrap.Component
40530  * @abstract
40531  * Base class for layout managers.
40532  */
40533 Roo.bootstrap.layout.Manager = function(config)
40534 {
40535     this.monitorWindowResize = true; // do this before we apply configuration.
40536     
40537     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
40538
40539
40540
40541
40542
40543     /** false to disable window resize monitoring @type Boolean */
40544     
40545     this.regions = {};
40546     this.addEvents({
40547         /**
40548          * @event layout
40549          * Fires when a layout is performed.
40550          * @param {Roo.LayoutManager} this
40551          */
40552         "layout" : true,
40553         /**
40554          * @event regionresized
40555          * Fires when the user resizes a region.
40556          * @param {Roo.LayoutRegion} region The resized region
40557          * @param {Number} newSize The new size (width for east/west, height for north/south)
40558          */
40559         "regionresized" : true,
40560         /**
40561          * @event regioncollapsed
40562          * Fires when a region is collapsed.
40563          * @param {Roo.LayoutRegion} region The collapsed region
40564          */
40565         "regioncollapsed" : true,
40566         /**
40567          * @event regionexpanded
40568          * Fires when a region is expanded.
40569          * @param {Roo.LayoutRegion} region The expanded region
40570          */
40571         "regionexpanded" : true
40572     });
40573     this.updating = false;
40574
40575     if (config.el) {
40576         this.el = Roo.get(config.el);
40577         this.initEvents();
40578     }
40579
40580 };
40581
40582 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
40583
40584
40585     regions : null,
40586
40587     monitorWindowResize : true,
40588
40589
40590     updating : false,
40591
40592
40593     onRender : function(ct, position)
40594     {
40595         if(!this.el){
40596             this.el = Roo.get(ct);
40597             this.initEvents();
40598         }
40599         //this.fireEvent('render',this);
40600     },
40601
40602
40603     initEvents: function()
40604     {
40605
40606
40607         // ie scrollbar fix
40608         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
40609             document.body.scroll = "no";
40610         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
40611             this.el.position('relative');
40612         }
40613         this.id = this.el.id;
40614         this.el.addClass("roo-layout-container");
40615         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
40616         if(this.el.dom != document.body ) {
40617             this.el.on('resize', this.layout,this);
40618             this.el.on('show', this.layout,this);
40619         }
40620
40621     },
40622
40623     /**
40624      * Returns true if this layout is currently being updated
40625      * @return {Boolean}
40626      */
40627     isUpdating : function(){
40628         return this.updating;
40629     },
40630
40631     /**
40632      * Suspend the LayoutManager from doing auto-layouts while
40633      * making multiple add or remove calls
40634      */
40635     beginUpdate : function(){
40636         this.updating = true;
40637     },
40638
40639     /**
40640      * Restore auto-layouts and optionally disable the manager from performing a layout
40641      * @param {Boolean} noLayout true to disable a layout update
40642      */
40643     endUpdate : function(noLayout){
40644         this.updating = false;
40645         if(!noLayout){
40646             this.layout();
40647         }
40648     },
40649
40650     layout: function(){
40651         // abstract...
40652     },
40653
40654     onRegionResized : function(region, newSize){
40655         this.fireEvent("regionresized", region, newSize);
40656         this.layout();
40657     },
40658
40659     onRegionCollapsed : function(region){
40660         this.fireEvent("regioncollapsed", region);
40661     },
40662
40663     onRegionExpanded : function(region){
40664         this.fireEvent("regionexpanded", region);
40665     },
40666
40667     /**
40668      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
40669      * performs box-model adjustments.
40670      * @return {Object} The size as an object {width: (the width), height: (the height)}
40671      */
40672     getViewSize : function()
40673     {
40674         var size;
40675         if(this.el.dom != document.body){
40676             size = this.el.getSize();
40677         }else{
40678             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
40679         }
40680         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
40681         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40682         return size;
40683     },
40684
40685     /**
40686      * Returns the Element this layout is bound to.
40687      * @return {Roo.Element}
40688      */
40689     getEl : function(){
40690         return this.el;
40691     },
40692
40693     /**
40694      * Returns the specified region.
40695      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
40696      * @return {Roo.LayoutRegion}
40697      */
40698     getRegion : function(target){
40699         return this.regions[target.toLowerCase()];
40700     },
40701
40702     onWindowResize : function(){
40703         if(this.monitorWindowResize){
40704             this.layout();
40705         }
40706     }
40707 });
40708 /*
40709  * Based on:
40710  * Ext JS Library 1.1.1
40711  * Copyright(c) 2006-2007, Ext JS, LLC.
40712  *
40713  * Originally Released Under LGPL - original licence link has changed is not relivant.
40714  *
40715  * Fork - LGPL
40716  * <script type="text/javascript">
40717  */
40718 /**
40719  * @class Roo.bootstrap.layout.Border
40720  * @extends Roo.bootstrap.layout.Manager
40721  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
40722  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
40723  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
40724  * please see: examples/bootstrap/nested.html<br><br>
40725  
40726 <b>The container the layout is rendered into can be either the body element or any other element.
40727 If it is not the body element, the container needs to either be an absolute positioned element,
40728 or you will need to add "position:relative" to the css of the container.  You will also need to specify
40729 the container size if it is not the body element.</b>
40730
40731 * @constructor
40732 * Create a new Border
40733 * @param {Object} config Configuration options
40734  */
40735 Roo.bootstrap.layout.Border = function(config){
40736     config = config || {};
40737     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
40738     
40739     
40740     
40741     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
40742         if(config[region]){
40743             config[region].region = region;
40744             this.addRegion(config[region]);
40745         }
40746     },this);
40747     
40748 };
40749
40750 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
40751
40752 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
40753     
40754         /**
40755          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
40756          */
40757         /**
40758          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
40759          */
40760         /**
40761          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
40762          */
40763         /**
40764          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
40765          */
40766         /**
40767          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
40768          */
40769         
40770         
40771         
40772         
40773     parent : false, // this might point to a 'nest' or a ???
40774     
40775     /**
40776      * Creates and adds a new region if it doesn't already exist.
40777      * @param {String} target The target region key (north, south, east, west or center).
40778      * @param {Object} config The regions config object
40779      * @return {BorderLayoutRegion} The new region
40780      */
40781     addRegion : function(config)
40782     {
40783         if(!this.regions[config.region]){
40784             var r = this.factory(config);
40785             this.bindRegion(r);
40786         }
40787         return this.regions[config.region];
40788     },
40789
40790     // private (kinda)
40791     bindRegion : function(r){
40792         this.regions[r.config.region] = r;
40793         
40794         r.on("visibilitychange",    this.layout, this);
40795         r.on("paneladded",          this.layout, this);
40796         r.on("panelremoved",        this.layout, this);
40797         r.on("invalidated",         this.layout, this);
40798         r.on("resized",             this.onRegionResized, this);
40799         r.on("collapsed",           this.onRegionCollapsed, this);
40800         r.on("expanded",            this.onRegionExpanded, this);
40801     },
40802
40803     /**
40804      * Performs a layout update.
40805      */
40806     layout : function()
40807     {
40808         if(this.updating) {
40809             return;
40810         }
40811         
40812         // render all the rebions if they have not been done alreayd?
40813         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
40814             if(this.regions[region] && !this.regions[region].bodyEl){
40815                 this.regions[region].onRender(this.el)
40816             }
40817         },this);
40818         
40819         var size = this.getViewSize();
40820         var w = size.width;
40821         var h = size.height;
40822         var centerW = w;
40823         var centerH = h;
40824         var centerY = 0;
40825         var centerX = 0;
40826         //var x = 0, y = 0;
40827
40828         var rs = this.regions;
40829         var north = rs["north"];
40830         var south = rs["south"]; 
40831         var west = rs["west"];
40832         var east = rs["east"];
40833         var center = rs["center"];
40834         //if(this.hideOnLayout){ // not supported anymore
40835             //c.el.setStyle("display", "none");
40836         //}
40837         if(north && north.isVisible()){
40838             var b = north.getBox();
40839             var m = north.getMargins();
40840             b.width = w - (m.left+m.right);
40841             b.x = m.left;
40842             b.y = m.top;
40843             centerY = b.height + b.y + m.bottom;
40844             centerH -= centerY;
40845             north.updateBox(this.safeBox(b));
40846         }
40847         if(south && south.isVisible()){
40848             var b = south.getBox();
40849             var m = south.getMargins();
40850             b.width = w - (m.left+m.right);
40851             b.x = m.left;
40852             var totalHeight = (b.height + m.top + m.bottom);
40853             b.y = h - totalHeight + m.top;
40854             centerH -= totalHeight;
40855             south.updateBox(this.safeBox(b));
40856         }
40857         if(west && west.isVisible()){
40858             var b = west.getBox();
40859             var m = west.getMargins();
40860             b.height = centerH - (m.top+m.bottom);
40861             b.x = m.left;
40862             b.y = centerY + m.top;
40863             var totalWidth = (b.width + m.left + m.right);
40864             centerX += totalWidth;
40865             centerW -= totalWidth;
40866             west.updateBox(this.safeBox(b));
40867         }
40868         if(east && east.isVisible()){
40869             var b = east.getBox();
40870             var m = east.getMargins();
40871             b.height = centerH - (m.top+m.bottom);
40872             var totalWidth = (b.width + m.left + m.right);
40873             b.x = w - totalWidth + m.left;
40874             b.y = centerY + m.top;
40875             centerW -= totalWidth;
40876             east.updateBox(this.safeBox(b));
40877         }
40878         if(center){
40879             var m = center.getMargins();
40880             var centerBox = {
40881                 x: centerX + m.left,
40882                 y: centerY + m.top,
40883                 width: centerW - (m.left+m.right),
40884                 height: centerH - (m.top+m.bottom)
40885             };
40886             //if(this.hideOnLayout){
40887                 //center.el.setStyle("display", "block");
40888             //}
40889             center.updateBox(this.safeBox(centerBox));
40890         }
40891         this.el.repaint();
40892         this.fireEvent("layout", this);
40893     },
40894
40895     // private
40896     safeBox : function(box){
40897         box.width = Math.max(0, box.width);
40898         box.height = Math.max(0, box.height);
40899         return box;
40900     },
40901
40902     /**
40903      * Adds a ContentPanel (or subclass) to this layout.
40904      * @param {String} target The target region key (north, south, east, west or center).
40905      * @param {Roo.ContentPanel} panel The panel to add
40906      * @return {Roo.ContentPanel} The added panel
40907      */
40908     add : function(target, panel){
40909          
40910         target = target.toLowerCase();
40911         return this.regions[target].add(panel);
40912     },
40913
40914     /**
40915      * Remove a ContentPanel (or subclass) to this layout.
40916      * @param {String} target The target region key (north, south, east, west or center).
40917      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
40918      * @return {Roo.ContentPanel} The removed panel
40919      */
40920     remove : function(target, panel){
40921         target = target.toLowerCase();
40922         return this.regions[target].remove(panel);
40923     },
40924
40925     /**
40926      * Searches all regions for a panel with the specified id
40927      * @param {String} panelId
40928      * @return {Roo.ContentPanel} The panel or null if it wasn't found
40929      */
40930     findPanel : function(panelId){
40931         var rs = this.regions;
40932         for(var target in rs){
40933             if(typeof rs[target] != "function"){
40934                 var p = rs[target].getPanel(panelId);
40935                 if(p){
40936                     return p;
40937                 }
40938             }
40939         }
40940         return null;
40941     },
40942
40943     /**
40944      * Searches all regions for a panel with the specified id and activates (shows) it.
40945      * @param {String/ContentPanel} panelId The panels id or the panel itself
40946      * @return {Roo.ContentPanel} The shown panel or null
40947      */
40948     showPanel : function(panelId) {
40949       var rs = this.regions;
40950       for(var target in rs){
40951          var r = rs[target];
40952          if(typeof r != "function"){
40953             if(r.hasPanel(panelId)){
40954                return r.showPanel(panelId);
40955             }
40956          }
40957       }
40958       return null;
40959    },
40960
40961    /**
40962      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
40963      * @param {Roo.state.Provider} provider (optional) An alternate state provider
40964      */
40965    /*
40966     restoreState : function(provider){
40967         if(!provider){
40968             provider = Roo.state.Manager;
40969         }
40970         var sm = new Roo.LayoutStateManager();
40971         sm.init(this, provider);
40972     },
40973 */
40974  
40975  
40976     /**
40977      * Adds a xtype elements to the layout.
40978      * <pre><code>
40979
40980 layout.addxtype({
40981        xtype : 'ContentPanel',
40982        region: 'west',
40983        items: [ .... ]
40984    }
40985 );
40986
40987 layout.addxtype({
40988         xtype : 'NestedLayoutPanel',
40989         region: 'west',
40990         layout: {
40991            center: { },
40992            west: { }   
40993         },
40994         items : [ ... list of content panels or nested layout panels.. ]
40995    }
40996 );
40997 </code></pre>
40998      * @param {Object} cfg Xtype definition of item to add.
40999      */
41000     addxtype : function(cfg)
41001     {
41002         // basically accepts a pannel...
41003         // can accept a layout region..!?!?
41004         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
41005         
41006         
41007         // theory?  children can only be panels??
41008         
41009         //if (!cfg.xtype.match(/Panel$/)) {
41010         //    return false;
41011         //}
41012         var ret = false;
41013         
41014         if (typeof(cfg.region) == 'undefined') {
41015             Roo.log("Failed to add Panel, region was not set");
41016             Roo.log(cfg);
41017             return false;
41018         }
41019         var region = cfg.region;
41020         delete cfg.region;
41021         
41022           
41023         var xitems = [];
41024         if (cfg.items) {
41025             xitems = cfg.items;
41026             delete cfg.items;
41027         }
41028         var nb = false;
41029         
41030         if ( region == 'center') {
41031             Roo.log("Center: " + cfg.title);
41032         }
41033         
41034         
41035         switch(cfg.xtype) 
41036         {
41037             case 'Content':  // ContentPanel (el, cfg)
41038             case 'Scroll':  // ContentPanel (el, cfg)
41039             case 'View': 
41040                 cfg.autoCreate = cfg.autoCreate || true;
41041                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41042                 //} else {
41043                 //    var el = this.el.createChild();
41044                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
41045                 //}
41046                 
41047                 this.add(region, ret);
41048                 break;
41049             
41050             /*
41051             case 'TreePanel': // our new panel!
41052                 cfg.el = this.el.createChild();
41053                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41054                 this.add(region, ret);
41055                 break;
41056             */
41057             
41058             case 'Nest': 
41059                 // create a new Layout (which is  a Border Layout...
41060                 
41061                 var clayout = cfg.layout;
41062                 clayout.el  = this.el.createChild();
41063                 clayout.items   = clayout.items  || [];
41064                 
41065                 delete cfg.layout;
41066                 
41067                 // replace this exitems with the clayout ones..
41068                 xitems = clayout.items;
41069                  
41070                 // force background off if it's in center...
41071                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
41072                     cfg.background = false;
41073                 }
41074                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
41075                 
41076                 
41077                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41078                 //console.log('adding nested layout panel '  + cfg.toSource());
41079                 this.add(region, ret);
41080                 nb = {}; /// find first...
41081                 break;
41082             
41083             case 'Grid':
41084                 
41085                 // needs grid and region
41086                 
41087                 //var el = this.getRegion(region).el.createChild();
41088                 /*
41089                  *var el = this.el.createChild();
41090                 // create the grid first...
41091                 cfg.grid.container = el;
41092                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
41093                 */
41094                 
41095                 if (region == 'center' && this.active ) {
41096                     cfg.background = false;
41097                 }
41098                 
41099                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41100                 
41101                 this.add(region, ret);
41102                 /*
41103                 if (cfg.background) {
41104                     // render grid on panel activation (if panel background)
41105                     ret.on('activate', function(gp) {
41106                         if (!gp.grid.rendered) {
41107                     //        gp.grid.render(el);
41108                         }
41109                     });
41110                 } else {
41111                   //  cfg.grid.render(el);
41112                 }
41113                 */
41114                 break;
41115            
41116            
41117             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
41118                 // it was the old xcomponent building that caused this before.
41119                 // espeically if border is the top element in the tree.
41120                 ret = this;
41121                 break; 
41122                 
41123                     
41124                 
41125                 
41126                 
41127             default:
41128                 /*
41129                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
41130                     
41131                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41132                     this.add(region, ret);
41133                 } else {
41134                 */
41135                     Roo.log(cfg);
41136                     throw "Can not add '" + cfg.xtype + "' to Border";
41137                     return null;
41138              
41139                                 
41140              
41141         }
41142         this.beginUpdate();
41143         // add children..
41144         var region = '';
41145         var abn = {};
41146         Roo.each(xitems, function(i)  {
41147             region = nb && i.region ? i.region : false;
41148             
41149             var add = ret.addxtype(i);
41150            
41151             if (region) {
41152                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
41153                 if (!i.background) {
41154                     abn[region] = nb[region] ;
41155                 }
41156             }
41157             
41158         });
41159         this.endUpdate();
41160
41161         // make the last non-background panel active..
41162         //if (nb) { Roo.log(abn); }
41163         if (nb) {
41164             
41165             for(var r in abn) {
41166                 region = this.getRegion(r);
41167                 if (region) {
41168                     // tried using nb[r], but it does not work..
41169                      
41170                     region.showPanel(abn[r]);
41171                    
41172                 }
41173             }
41174         }
41175         return ret;
41176         
41177     },
41178     
41179     
41180 // private
41181     factory : function(cfg)
41182     {
41183         
41184         var validRegions = Roo.bootstrap.layout.Border.regions;
41185
41186         var target = cfg.region;
41187         cfg.mgr = this;
41188         
41189         var r = Roo.bootstrap.layout;
41190         Roo.log(target);
41191         switch(target){
41192             case "north":
41193                 return new r.North(cfg);
41194             case "south":
41195                 return new r.South(cfg);
41196             case "east":
41197                 return new r.East(cfg);
41198             case "west":
41199                 return new r.West(cfg);
41200             case "center":
41201                 return new r.Center(cfg);
41202         }
41203         throw 'Layout region "'+target+'" not supported.';
41204     }
41205     
41206     
41207 });
41208  /*
41209  * Based on:
41210  * Ext JS Library 1.1.1
41211  * Copyright(c) 2006-2007, Ext JS, LLC.
41212  *
41213  * Originally Released Under LGPL - original licence link has changed is not relivant.
41214  *
41215  * Fork - LGPL
41216  * <script type="text/javascript">
41217  */
41218  
41219 /**
41220  * @class Roo.bootstrap.layout.Basic
41221  * @extends Roo.util.Observable
41222  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
41223  * and does not have a titlebar, tabs or any other features. All it does is size and position 
41224  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
41225  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
41226  * @cfg {string}   region  the region that it inhabits..
41227  * @cfg {bool}   skipConfig skip config?
41228  * 
41229
41230  */
41231 Roo.bootstrap.layout.Basic = function(config){
41232     
41233     this.mgr = config.mgr;
41234     
41235     this.position = config.region;
41236     
41237     var skipConfig = config.skipConfig;
41238     
41239     this.events = {
41240         /**
41241          * @scope Roo.BasicLayoutRegion
41242          */
41243         
41244         /**
41245          * @event beforeremove
41246          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
41247          * @param {Roo.LayoutRegion} this
41248          * @param {Roo.ContentPanel} panel The panel
41249          * @param {Object} e The cancel event object
41250          */
41251         "beforeremove" : true,
41252         /**
41253          * @event invalidated
41254          * Fires when the layout for this region is changed.
41255          * @param {Roo.LayoutRegion} this
41256          */
41257         "invalidated" : true,
41258         /**
41259          * @event visibilitychange
41260          * Fires when this region is shown or hidden 
41261          * @param {Roo.LayoutRegion} this
41262          * @param {Boolean} visibility true or false
41263          */
41264         "visibilitychange" : true,
41265         /**
41266          * @event paneladded
41267          * Fires when a panel is added. 
41268          * @param {Roo.LayoutRegion} this
41269          * @param {Roo.ContentPanel} panel The panel
41270          */
41271         "paneladded" : true,
41272         /**
41273          * @event panelremoved
41274          * Fires when a panel is removed. 
41275          * @param {Roo.LayoutRegion} this
41276          * @param {Roo.ContentPanel} panel The panel
41277          */
41278         "panelremoved" : true,
41279         /**
41280          * @event beforecollapse
41281          * Fires when this region before collapse.
41282          * @param {Roo.LayoutRegion} this
41283          */
41284         "beforecollapse" : true,
41285         /**
41286          * @event collapsed
41287          * Fires when this region is collapsed.
41288          * @param {Roo.LayoutRegion} this
41289          */
41290         "collapsed" : true,
41291         /**
41292          * @event expanded
41293          * Fires when this region is expanded.
41294          * @param {Roo.LayoutRegion} this
41295          */
41296         "expanded" : true,
41297         /**
41298          * @event slideshow
41299          * Fires when this region is slid into view.
41300          * @param {Roo.LayoutRegion} this
41301          */
41302         "slideshow" : true,
41303         /**
41304          * @event slidehide
41305          * Fires when this region slides out of view. 
41306          * @param {Roo.LayoutRegion} this
41307          */
41308         "slidehide" : true,
41309         /**
41310          * @event panelactivated
41311          * Fires when a panel is activated. 
41312          * @param {Roo.LayoutRegion} this
41313          * @param {Roo.ContentPanel} panel The activated panel
41314          */
41315         "panelactivated" : true,
41316         /**
41317          * @event resized
41318          * Fires when the user resizes this region. 
41319          * @param {Roo.LayoutRegion} this
41320          * @param {Number} newSize The new size (width for east/west, height for north/south)
41321          */
41322         "resized" : true
41323     };
41324     /** A collection of panels in this region. @type Roo.util.MixedCollection */
41325     this.panels = new Roo.util.MixedCollection();
41326     this.panels.getKey = this.getPanelId.createDelegate(this);
41327     this.box = null;
41328     this.activePanel = null;
41329     // ensure listeners are added...
41330     
41331     if (config.listeners || config.events) {
41332         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
41333             listeners : config.listeners || {},
41334             events : config.events || {}
41335         });
41336     }
41337     
41338     if(skipConfig !== true){
41339         this.applyConfig(config);
41340     }
41341 };
41342
41343 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
41344 {
41345     getPanelId : function(p){
41346         return p.getId();
41347     },
41348     
41349     applyConfig : function(config){
41350         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41351         this.config = config;
41352         
41353     },
41354     
41355     /**
41356      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
41357      * the width, for horizontal (north, south) the height.
41358      * @param {Number} newSize The new width or height
41359      */
41360     resizeTo : function(newSize){
41361         var el = this.el ? this.el :
41362                  (this.activePanel ? this.activePanel.getEl() : null);
41363         if(el){
41364             switch(this.position){
41365                 case "east":
41366                 case "west":
41367                     el.setWidth(newSize);
41368                     this.fireEvent("resized", this, newSize);
41369                 break;
41370                 case "north":
41371                 case "south":
41372                     el.setHeight(newSize);
41373                     this.fireEvent("resized", this, newSize);
41374                 break;                
41375             }
41376         }
41377     },
41378     
41379     getBox : function(){
41380         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
41381     },
41382     
41383     getMargins : function(){
41384         return this.margins;
41385     },
41386     
41387     updateBox : function(box){
41388         this.box = box;
41389         var el = this.activePanel.getEl();
41390         el.dom.style.left = box.x + "px";
41391         el.dom.style.top = box.y + "px";
41392         this.activePanel.setSize(box.width, box.height);
41393     },
41394     
41395     /**
41396      * Returns the container element for this region.
41397      * @return {Roo.Element}
41398      */
41399     getEl : function(){
41400         return this.activePanel;
41401     },
41402     
41403     /**
41404      * Returns true if this region is currently visible.
41405      * @return {Boolean}
41406      */
41407     isVisible : function(){
41408         return this.activePanel ? true : false;
41409     },
41410     
41411     setActivePanel : function(panel){
41412         panel = this.getPanel(panel);
41413         if(this.activePanel && this.activePanel != panel){
41414             this.activePanel.setActiveState(false);
41415             this.activePanel.getEl().setLeftTop(-10000,-10000);
41416         }
41417         this.activePanel = panel;
41418         panel.setActiveState(true);
41419         if(this.box){
41420             panel.setSize(this.box.width, this.box.height);
41421         }
41422         this.fireEvent("panelactivated", this, panel);
41423         this.fireEvent("invalidated");
41424     },
41425     
41426     /**
41427      * Show the specified panel.
41428      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
41429      * @return {Roo.ContentPanel} The shown panel or null
41430      */
41431     showPanel : function(panel){
41432         panel = this.getPanel(panel);
41433         if(panel){
41434             this.setActivePanel(panel);
41435         }
41436         return panel;
41437     },
41438     
41439     /**
41440      * Get the active panel for this region.
41441      * @return {Roo.ContentPanel} The active panel or null
41442      */
41443     getActivePanel : function(){
41444         return this.activePanel;
41445     },
41446     
41447     /**
41448      * Add the passed ContentPanel(s)
41449      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
41450      * @return {Roo.ContentPanel} The panel added (if only one was added)
41451      */
41452     add : function(panel){
41453         if(arguments.length > 1){
41454             for(var i = 0, len = arguments.length; i < len; i++) {
41455                 this.add(arguments[i]);
41456             }
41457             return null;
41458         }
41459         if(this.hasPanel(panel)){
41460             this.showPanel(panel);
41461             return panel;
41462         }
41463         var el = panel.getEl();
41464         if(el.dom.parentNode != this.mgr.el.dom){
41465             this.mgr.el.dom.appendChild(el.dom);
41466         }
41467         if(panel.setRegion){
41468             panel.setRegion(this);
41469         }
41470         this.panels.add(panel);
41471         el.setStyle("position", "absolute");
41472         if(!panel.background){
41473             this.setActivePanel(panel);
41474             if(this.config.initialSize && this.panels.getCount()==1){
41475                 this.resizeTo(this.config.initialSize);
41476             }
41477         }
41478         this.fireEvent("paneladded", this, panel);
41479         return panel;
41480     },
41481     
41482     /**
41483      * Returns true if the panel is in this region.
41484      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41485      * @return {Boolean}
41486      */
41487     hasPanel : function(panel){
41488         if(typeof panel == "object"){ // must be panel obj
41489             panel = panel.getId();
41490         }
41491         return this.getPanel(panel) ? true : false;
41492     },
41493     
41494     /**
41495      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
41496      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41497      * @param {Boolean} preservePanel Overrides the config preservePanel option
41498      * @return {Roo.ContentPanel} The panel that was removed
41499      */
41500     remove : function(panel, preservePanel){
41501         panel = this.getPanel(panel);
41502         if(!panel){
41503             return null;
41504         }
41505         var e = {};
41506         this.fireEvent("beforeremove", this, panel, e);
41507         if(e.cancel === true){
41508             return null;
41509         }
41510         var panelId = panel.getId();
41511         this.panels.removeKey(panelId);
41512         return panel;
41513     },
41514     
41515     /**
41516      * Returns the panel specified or null if it's not in this region.
41517      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41518      * @return {Roo.ContentPanel}
41519      */
41520     getPanel : function(id){
41521         if(typeof id == "object"){ // must be panel obj
41522             return id;
41523         }
41524         return this.panels.get(id);
41525     },
41526     
41527     /**
41528      * Returns this regions position (north/south/east/west/center).
41529      * @return {String} 
41530      */
41531     getPosition: function(){
41532         return this.position;    
41533     }
41534 });/*
41535  * Based on:
41536  * Ext JS Library 1.1.1
41537  * Copyright(c) 2006-2007, Ext JS, LLC.
41538  *
41539  * Originally Released Under LGPL - original licence link has changed is not relivant.
41540  *
41541  * Fork - LGPL
41542  * <script type="text/javascript">
41543  */
41544  
41545 /**
41546  * @class Roo.bootstrap.layout.Region
41547  * @extends Roo.bootstrap.layout.Basic
41548  * This class represents a region in a layout manager.
41549  
41550  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
41551  * @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})
41552  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
41553  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
41554  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
41555  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
41556  * @cfg {String}    title           The title for the region (overrides panel titles)
41557  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
41558  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
41559  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
41560  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
41561  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
41562  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
41563  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
41564  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
41565  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
41566  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
41567
41568  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
41569  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
41570  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
41571  * @cfg {Number}    width           For East/West panels
41572  * @cfg {Number}    height          For North/South panels
41573  * @cfg {Boolean}   split           To show the splitter
41574  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
41575  * 
41576  * @cfg {string}   cls             Extra CSS classes to add to region
41577  * 
41578  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
41579  * @cfg {string}   region  the region that it inhabits..
41580  *
41581
41582  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
41583  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
41584
41585  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
41586  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
41587  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
41588  */
41589 Roo.bootstrap.layout.Region = function(config)
41590 {
41591     this.applyConfig(config);
41592
41593     var mgr = config.mgr;
41594     var pos = config.region;
41595     config.skipConfig = true;
41596     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
41597     
41598     if (mgr.el) {
41599         this.onRender(mgr.el);   
41600     }
41601      
41602     this.visible = true;
41603     this.collapsed = false;
41604     this.unrendered_panels = [];
41605 };
41606
41607 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
41608
41609     position: '', // set by wrapper (eg. north/south etc..)
41610     unrendered_panels : null,  // unrendered panels.
41611     
41612     tabPosition : false,
41613     
41614     mgr: false, // points to 'Border'
41615     
41616     
41617     createBody : function(){
41618         /** This region's body element 
41619         * @type Roo.Element */
41620         this.bodyEl = this.el.createChild({
41621                 tag: "div",
41622                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
41623         });
41624     },
41625
41626     onRender: function(ctr, pos)
41627     {
41628         var dh = Roo.DomHelper;
41629         /** This region's container element 
41630         * @type Roo.Element */
41631         this.el = dh.append(ctr.dom, {
41632                 tag: "div",
41633                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
41634             }, true);
41635         /** This region's title element 
41636         * @type Roo.Element */
41637     
41638         this.titleEl = dh.append(this.el.dom,  {
41639                 tag: "div",
41640                 unselectable: "on",
41641                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
41642                 children:[
41643                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
41644                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
41645                 ]
41646             }, true);
41647         
41648         this.titleEl.enableDisplayMode();
41649         /** This region's title text element 
41650         * @type HTMLElement */
41651         this.titleTextEl = this.titleEl.dom.firstChild;
41652         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
41653         /*
41654         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
41655         this.closeBtn.enableDisplayMode();
41656         this.closeBtn.on("click", this.closeClicked, this);
41657         this.closeBtn.hide();
41658     */
41659         this.createBody(this.config);
41660         if(this.config.hideWhenEmpty){
41661             this.hide();
41662             this.on("paneladded", this.validateVisibility, this);
41663             this.on("panelremoved", this.validateVisibility, this);
41664         }
41665         if(this.autoScroll){
41666             this.bodyEl.setStyle("overflow", "auto");
41667         }else{
41668             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
41669         }
41670         //if(c.titlebar !== false){
41671             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
41672                 this.titleEl.hide();
41673             }else{
41674                 this.titleEl.show();
41675                 if(this.config.title){
41676                     this.titleTextEl.innerHTML = this.config.title;
41677                 }
41678             }
41679         //}
41680         if(this.config.collapsed){
41681             this.collapse(true);
41682         }
41683         if(this.config.hidden){
41684             this.hide();
41685         }
41686         
41687         if (this.unrendered_panels && this.unrendered_panels.length) {
41688             for (var i =0;i< this.unrendered_panels.length; i++) {
41689                 this.add(this.unrendered_panels[i]);
41690             }
41691             this.unrendered_panels = null;
41692             
41693         }
41694         
41695     },
41696     
41697     applyConfig : function(c)
41698     {
41699         /*
41700          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
41701             var dh = Roo.DomHelper;
41702             if(c.titlebar !== false){
41703                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
41704                 this.collapseBtn.on("click", this.collapse, this);
41705                 this.collapseBtn.enableDisplayMode();
41706                 /*
41707                 if(c.showPin === true || this.showPin){
41708                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
41709                     this.stickBtn.enableDisplayMode();
41710                     this.stickBtn.on("click", this.expand, this);
41711                     this.stickBtn.hide();
41712                 }
41713                 
41714             }
41715             */
41716             /** This region's collapsed element
41717             * @type Roo.Element */
41718             /*
41719              *
41720             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
41721                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
41722             ]}, true);
41723             
41724             if(c.floatable !== false){
41725                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
41726                this.collapsedEl.on("click", this.collapseClick, this);
41727             }
41728
41729             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
41730                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
41731                    id: "message", unselectable: "on", style:{"float":"left"}});
41732                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
41733              }
41734             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
41735             this.expandBtn.on("click", this.expand, this);
41736             
41737         }
41738         
41739         if(this.collapseBtn){
41740             this.collapseBtn.setVisible(c.collapsible == true);
41741         }
41742         
41743         this.cmargins = c.cmargins || this.cmargins ||
41744                          (this.position == "west" || this.position == "east" ?
41745                              {top: 0, left: 2, right:2, bottom: 0} :
41746                              {top: 2, left: 0, right:0, bottom: 2});
41747         */
41748         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41749         
41750         
41751         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
41752         
41753         this.autoScroll = c.autoScroll || false;
41754         
41755         
41756        
41757         
41758         this.duration = c.duration || .30;
41759         this.slideDuration = c.slideDuration || .45;
41760         this.config = c;
41761        
41762     },
41763     /**
41764      * Returns true if this region is currently visible.
41765      * @return {Boolean}
41766      */
41767     isVisible : function(){
41768         return this.visible;
41769     },
41770
41771     /**
41772      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
41773      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
41774      */
41775     //setCollapsedTitle : function(title){
41776     //    title = title || "&#160;";
41777      //   if(this.collapsedTitleTextEl){
41778       //      this.collapsedTitleTextEl.innerHTML = title;
41779        // }
41780     //},
41781
41782     getBox : function(){
41783         var b;
41784       //  if(!this.collapsed){
41785             b = this.el.getBox(false, true);
41786        // }else{
41787           //  b = this.collapsedEl.getBox(false, true);
41788         //}
41789         return b;
41790     },
41791
41792     getMargins : function(){
41793         return this.margins;
41794         //return this.collapsed ? this.cmargins : this.margins;
41795     },
41796 /*
41797     highlight : function(){
41798         this.el.addClass("x-layout-panel-dragover");
41799     },
41800
41801     unhighlight : function(){
41802         this.el.removeClass("x-layout-panel-dragover");
41803     },
41804 */
41805     updateBox : function(box)
41806     {
41807         if (!this.bodyEl) {
41808             return; // not rendered yet..
41809         }
41810         
41811         this.box = box;
41812         if(!this.collapsed){
41813             this.el.dom.style.left = box.x + "px";
41814             this.el.dom.style.top = box.y + "px";
41815             this.updateBody(box.width, box.height);
41816         }else{
41817             this.collapsedEl.dom.style.left = box.x + "px";
41818             this.collapsedEl.dom.style.top = box.y + "px";
41819             this.collapsedEl.setSize(box.width, box.height);
41820         }
41821         if(this.tabs){
41822             this.tabs.autoSizeTabs();
41823         }
41824     },
41825
41826     updateBody : function(w, h)
41827     {
41828         if(w !== null){
41829             this.el.setWidth(w);
41830             w -= this.el.getBorderWidth("rl");
41831             if(this.config.adjustments){
41832                 w += this.config.adjustments[0];
41833             }
41834         }
41835         if(h !== null && h > 0){
41836             this.el.setHeight(h);
41837             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
41838             h -= this.el.getBorderWidth("tb");
41839             if(this.config.adjustments){
41840                 h += this.config.adjustments[1];
41841             }
41842             this.bodyEl.setHeight(h);
41843             if(this.tabs){
41844                 h = this.tabs.syncHeight(h);
41845             }
41846         }
41847         if(this.panelSize){
41848             w = w !== null ? w : this.panelSize.width;
41849             h = h !== null ? h : this.panelSize.height;
41850         }
41851         if(this.activePanel){
41852             var el = this.activePanel.getEl();
41853             w = w !== null ? w : el.getWidth();
41854             h = h !== null ? h : el.getHeight();
41855             this.panelSize = {width: w, height: h};
41856             this.activePanel.setSize(w, h);
41857         }
41858         if(Roo.isIE && this.tabs){
41859             this.tabs.el.repaint();
41860         }
41861     },
41862
41863     /**
41864      * Returns the container element for this region.
41865      * @return {Roo.Element}
41866      */
41867     getEl : function(){
41868         return this.el;
41869     },
41870
41871     /**
41872      * Hides this region.
41873      */
41874     hide : function(){
41875         //if(!this.collapsed){
41876             this.el.dom.style.left = "-2000px";
41877             this.el.hide();
41878         //}else{
41879          //   this.collapsedEl.dom.style.left = "-2000px";
41880          //   this.collapsedEl.hide();
41881        // }
41882         this.visible = false;
41883         this.fireEvent("visibilitychange", this, false);
41884     },
41885
41886     /**
41887      * Shows this region if it was previously hidden.
41888      */
41889     show : function(){
41890         //if(!this.collapsed){
41891             this.el.show();
41892         //}else{
41893         //    this.collapsedEl.show();
41894        // }
41895         this.visible = true;
41896         this.fireEvent("visibilitychange", this, true);
41897     },
41898 /*
41899     closeClicked : function(){
41900         if(this.activePanel){
41901             this.remove(this.activePanel);
41902         }
41903     },
41904
41905     collapseClick : function(e){
41906         if(this.isSlid){
41907            e.stopPropagation();
41908            this.slideIn();
41909         }else{
41910            e.stopPropagation();
41911            this.slideOut();
41912         }
41913     },
41914 */
41915     /**
41916      * Collapses this region.
41917      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
41918      */
41919     /*
41920     collapse : function(skipAnim, skipCheck = false){
41921         if(this.collapsed) {
41922             return;
41923         }
41924         
41925         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
41926             
41927             this.collapsed = true;
41928             if(this.split){
41929                 this.split.el.hide();
41930             }
41931             if(this.config.animate && skipAnim !== true){
41932                 this.fireEvent("invalidated", this);
41933                 this.animateCollapse();
41934             }else{
41935                 this.el.setLocation(-20000,-20000);
41936                 this.el.hide();
41937                 this.collapsedEl.show();
41938                 this.fireEvent("collapsed", this);
41939                 this.fireEvent("invalidated", this);
41940             }
41941         }
41942         
41943     },
41944 */
41945     animateCollapse : function(){
41946         // overridden
41947     },
41948
41949     /**
41950      * Expands this region if it was previously collapsed.
41951      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
41952      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
41953      */
41954     /*
41955     expand : function(e, skipAnim){
41956         if(e) {
41957             e.stopPropagation();
41958         }
41959         if(!this.collapsed || this.el.hasActiveFx()) {
41960             return;
41961         }
41962         if(this.isSlid){
41963             this.afterSlideIn();
41964             skipAnim = true;
41965         }
41966         this.collapsed = false;
41967         if(this.config.animate && skipAnim !== true){
41968             this.animateExpand();
41969         }else{
41970             this.el.show();
41971             if(this.split){
41972                 this.split.el.show();
41973             }
41974             this.collapsedEl.setLocation(-2000,-2000);
41975             this.collapsedEl.hide();
41976             this.fireEvent("invalidated", this);
41977             this.fireEvent("expanded", this);
41978         }
41979     },
41980 */
41981     animateExpand : function(){
41982         // overridden
41983     },
41984
41985     initTabs : function()
41986     {
41987         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
41988         
41989         var ts = new Roo.bootstrap.panel.Tabs({
41990             el: this.bodyEl.dom,
41991             region : this,
41992             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
41993             disableTooltips: this.config.disableTabTips,
41994             toolbar : this.config.toolbar
41995         });
41996         
41997         if(this.config.hideTabs){
41998             ts.stripWrap.setDisplayed(false);
41999         }
42000         this.tabs = ts;
42001         ts.resizeTabs = this.config.resizeTabs === true;
42002         ts.minTabWidth = this.config.minTabWidth || 40;
42003         ts.maxTabWidth = this.config.maxTabWidth || 250;
42004         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
42005         ts.monitorResize = false;
42006         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
42007         ts.bodyEl.addClass('roo-layout-tabs-body');
42008         this.panels.each(this.initPanelAsTab, this);
42009     },
42010
42011     initPanelAsTab : function(panel){
42012         var ti = this.tabs.addTab(
42013             panel.getEl().id,
42014             panel.getTitle(),
42015             null,
42016             this.config.closeOnTab && panel.isClosable(),
42017             panel.tpl
42018         );
42019         if(panel.tabTip !== undefined){
42020             ti.setTooltip(panel.tabTip);
42021         }
42022         ti.on("activate", function(){
42023               this.setActivePanel(panel);
42024         }, this);
42025         
42026         if(this.config.closeOnTab){
42027             ti.on("beforeclose", function(t, e){
42028                 e.cancel = true;
42029                 this.remove(panel);
42030             }, this);
42031         }
42032         
42033         panel.tabItem = ti;
42034         
42035         return ti;
42036     },
42037
42038     updatePanelTitle : function(panel, title)
42039     {
42040         if(this.activePanel == panel){
42041             this.updateTitle(title);
42042         }
42043         if(this.tabs){
42044             var ti = this.tabs.getTab(panel.getEl().id);
42045             ti.setText(title);
42046             if(panel.tabTip !== undefined){
42047                 ti.setTooltip(panel.tabTip);
42048             }
42049         }
42050     },
42051
42052     updateTitle : function(title){
42053         if(this.titleTextEl && !this.config.title){
42054             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
42055         }
42056     },
42057
42058     setActivePanel : function(panel)
42059     {
42060         panel = this.getPanel(panel);
42061         if(this.activePanel && this.activePanel != panel){
42062             if(this.activePanel.setActiveState(false) === false){
42063                 return;
42064             }
42065         }
42066         this.activePanel = panel;
42067         panel.setActiveState(true);
42068         if(this.panelSize){
42069             panel.setSize(this.panelSize.width, this.panelSize.height);
42070         }
42071         if(this.closeBtn){
42072             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
42073         }
42074         this.updateTitle(panel.getTitle());
42075         if(this.tabs){
42076             this.fireEvent("invalidated", this);
42077         }
42078         this.fireEvent("panelactivated", this, panel);
42079     },
42080
42081     /**
42082      * Shows the specified panel.
42083      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
42084      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
42085      */
42086     showPanel : function(panel)
42087     {
42088         panel = this.getPanel(panel);
42089         if(panel){
42090             if(this.tabs){
42091                 var tab = this.tabs.getTab(panel.getEl().id);
42092                 if(tab.isHidden()){
42093                     this.tabs.unhideTab(tab.id);
42094                 }
42095                 tab.activate();
42096             }else{
42097                 this.setActivePanel(panel);
42098             }
42099         }
42100         return panel;
42101     },
42102
42103     /**
42104      * Get the active panel for this region.
42105      * @return {Roo.ContentPanel} The active panel or null
42106      */
42107     getActivePanel : function(){
42108         return this.activePanel;
42109     },
42110
42111     validateVisibility : function(){
42112         if(this.panels.getCount() < 1){
42113             this.updateTitle("&#160;");
42114             this.closeBtn.hide();
42115             this.hide();
42116         }else{
42117             if(!this.isVisible()){
42118                 this.show();
42119             }
42120         }
42121     },
42122
42123     /**
42124      * Adds the passed ContentPanel(s) to this region.
42125      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42126      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
42127      */
42128     add : function(panel)
42129     {
42130         if(arguments.length > 1){
42131             for(var i = 0, len = arguments.length; i < len; i++) {
42132                 this.add(arguments[i]);
42133             }
42134             return null;
42135         }
42136         
42137         // if we have not been rendered yet, then we can not really do much of this..
42138         if (!this.bodyEl) {
42139             this.unrendered_panels.push(panel);
42140             return panel;
42141         }
42142         
42143         
42144         
42145         
42146         if(this.hasPanel(panel)){
42147             this.showPanel(panel);
42148             return panel;
42149         }
42150         panel.setRegion(this);
42151         this.panels.add(panel);
42152        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
42153             // sinle panel - no tab...?? would it not be better to render it with the tabs,
42154             // and hide them... ???
42155             this.bodyEl.dom.appendChild(panel.getEl().dom);
42156             if(panel.background !== true){
42157                 this.setActivePanel(panel);
42158             }
42159             this.fireEvent("paneladded", this, panel);
42160             return panel;
42161         }
42162         */
42163         if(!this.tabs){
42164             this.initTabs();
42165         }else{
42166             this.initPanelAsTab(panel);
42167         }
42168         
42169         
42170         if(panel.background !== true){
42171             this.tabs.activate(panel.getEl().id);
42172         }
42173         this.fireEvent("paneladded", this, panel);
42174         return panel;
42175     },
42176
42177     /**
42178      * Hides the tab for the specified panel.
42179      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42180      */
42181     hidePanel : function(panel){
42182         if(this.tabs && (panel = this.getPanel(panel))){
42183             this.tabs.hideTab(panel.getEl().id);
42184         }
42185     },
42186
42187     /**
42188      * Unhides the tab for a previously hidden panel.
42189      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42190      */
42191     unhidePanel : function(panel){
42192         if(this.tabs && (panel = this.getPanel(panel))){
42193             this.tabs.unhideTab(panel.getEl().id);
42194         }
42195     },
42196
42197     clearPanels : function(){
42198         while(this.panels.getCount() > 0){
42199              this.remove(this.panels.first());
42200         }
42201     },
42202
42203     /**
42204      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
42205      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42206      * @param {Boolean} preservePanel Overrides the config preservePanel option
42207      * @return {Roo.ContentPanel} The panel that was removed
42208      */
42209     remove : function(panel, preservePanel)
42210     {
42211         panel = this.getPanel(panel);
42212         if(!panel){
42213             return null;
42214         }
42215         var e = {};
42216         this.fireEvent("beforeremove", this, panel, e);
42217         if(e.cancel === true){
42218             return null;
42219         }
42220         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
42221         var panelId = panel.getId();
42222         this.panels.removeKey(panelId);
42223         if(preservePanel){
42224             document.body.appendChild(panel.getEl().dom);
42225         }
42226         if(this.tabs){
42227             this.tabs.removeTab(panel.getEl().id);
42228         }else if (!preservePanel){
42229             this.bodyEl.dom.removeChild(panel.getEl().dom);
42230         }
42231         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
42232             var p = this.panels.first();
42233             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
42234             tempEl.appendChild(p.getEl().dom);
42235             this.bodyEl.update("");
42236             this.bodyEl.dom.appendChild(p.getEl().dom);
42237             tempEl = null;
42238             this.updateTitle(p.getTitle());
42239             this.tabs = null;
42240             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
42241             this.setActivePanel(p);
42242         }
42243         panel.setRegion(null);
42244         if(this.activePanel == panel){
42245             this.activePanel = null;
42246         }
42247         if(this.config.autoDestroy !== false && preservePanel !== true){
42248             try{panel.destroy();}catch(e){}
42249         }
42250         this.fireEvent("panelremoved", this, panel);
42251         return panel;
42252     },
42253
42254     /**
42255      * Returns the TabPanel component used by this region
42256      * @return {Roo.TabPanel}
42257      */
42258     getTabs : function(){
42259         return this.tabs;
42260     },
42261
42262     createTool : function(parentEl, className){
42263         var btn = Roo.DomHelper.append(parentEl, {
42264             tag: "div",
42265             cls: "x-layout-tools-button",
42266             children: [ {
42267                 tag: "div",
42268                 cls: "roo-layout-tools-button-inner " + className,
42269                 html: "&#160;"
42270             }]
42271         }, true);
42272         btn.addClassOnOver("roo-layout-tools-button-over");
42273         return btn;
42274     }
42275 });/*
42276  * Based on:
42277  * Ext JS Library 1.1.1
42278  * Copyright(c) 2006-2007, Ext JS, LLC.
42279  *
42280  * Originally Released Under LGPL - original licence link has changed is not relivant.
42281  *
42282  * Fork - LGPL
42283  * <script type="text/javascript">
42284  */
42285  
42286
42287
42288 /**
42289  * @class Roo.SplitLayoutRegion
42290  * @extends Roo.LayoutRegion
42291  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
42292  */
42293 Roo.bootstrap.layout.Split = function(config){
42294     this.cursor = config.cursor;
42295     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
42296 };
42297
42298 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
42299 {
42300     splitTip : "Drag to resize.",
42301     collapsibleSplitTip : "Drag to resize. Double click to hide.",
42302     useSplitTips : false,
42303
42304     applyConfig : function(config){
42305         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
42306     },
42307     
42308     onRender : function(ctr,pos) {
42309         
42310         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
42311         if(!this.config.split){
42312             return;
42313         }
42314         if(!this.split){
42315             
42316             var splitEl = Roo.DomHelper.append(ctr.dom,  {
42317                             tag: "div",
42318                             id: this.el.id + "-split",
42319                             cls: "roo-layout-split roo-layout-split-"+this.position,
42320                             html: "&#160;"
42321             });
42322             /** The SplitBar for this region 
42323             * @type Roo.SplitBar */
42324             // does not exist yet...
42325             Roo.log([this.position, this.orientation]);
42326             
42327             this.split = new Roo.bootstrap.SplitBar({
42328                 dragElement : splitEl,
42329                 resizingElement: this.el,
42330                 orientation : this.orientation
42331             });
42332             
42333             this.split.on("moved", this.onSplitMove, this);
42334             this.split.useShim = this.config.useShim === true;
42335             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
42336             if(this.useSplitTips){
42337                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
42338             }
42339             //if(config.collapsible){
42340             //    this.split.el.on("dblclick", this.collapse,  this);
42341             //}
42342         }
42343         if(typeof this.config.minSize != "undefined"){
42344             this.split.minSize = this.config.minSize;
42345         }
42346         if(typeof this.config.maxSize != "undefined"){
42347             this.split.maxSize = this.config.maxSize;
42348         }
42349         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
42350             this.hideSplitter();
42351         }
42352         
42353     },
42354
42355     getHMaxSize : function(){
42356          var cmax = this.config.maxSize || 10000;
42357          var center = this.mgr.getRegion("center");
42358          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
42359     },
42360
42361     getVMaxSize : function(){
42362          var cmax = this.config.maxSize || 10000;
42363          var center = this.mgr.getRegion("center");
42364          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
42365     },
42366
42367     onSplitMove : function(split, newSize){
42368         this.fireEvent("resized", this, newSize);
42369     },
42370     
42371     /** 
42372      * Returns the {@link Roo.SplitBar} for this region.
42373      * @return {Roo.SplitBar}
42374      */
42375     getSplitBar : function(){
42376         return this.split;
42377     },
42378     
42379     hide : function(){
42380         this.hideSplitter();
42381         Roo.bootstrap.layout.Split.superclass.hide.call(this);
42382     },
42383
42384     hideSplitter : function(){
42385         if(this.split){
42386             this.split.el.setLocation(-2000,-2000);
42387             this.split.el.hide();
42388         }
42389     },
42390
42391     show : function(){
42392         if(this.split){
42393             this.split.el.show();
42394         }
42395         Roo.bootstrap.layout.Split.superclass.show.call(this);
42396     },
42397     
42398     beforeSlide: function(){
42399         if(Roo.isGecko){// firefox overflow auto bug workaround
42400             this.bodyEl.clip();
42401             if(this.tabs) {
42402                 this.tabs.bodyEl.clip();
42403             }
42404             if(this.activePanel){
42405                 this.activePanel.getEl().clip();
42406                 
42407                 if(this.activePanel.beforeSlide){
42408                     this.activePanel.beforeSlide();
42409                 }
42410             }
42411         }
42412     },
42413     
42414     afterSlide : function(){
42415         if(Roo.isGecko){// firefox overflow auto bug workaround
42416             this.bodyEl.unclip();
42417             if(this.tabs) {
42418                 this.tabs.bodyEl.unclip();
42419             }
42420             if(this.activePanel){
42421                 this.activePanel.getEl().unclip();
42422                 if(this.activePanel.afterSlide){
42423                     this.activePanel.afterSlide();
42424                 }
42425             }
42426         }
42427     },
42428
42429     initAutoHide : function(){
42430         if(this.autoHide !== false){
42431             if(!this.autoHideHd){
42432                 var st = new Roo.util.DelayedTask(this.slideIn, this);
42433                 this.autoHideHd = {
42434                     "mouseout": function(e){
42435                         if(!e.within(this.el, true)){
42436                             st.delay(500);
42437                         }
42438                     },
42439                     "mouseover" : function(e){
42440                         st.cancel();
42441                     },
42442                     scope : this
42443                 };
42444             }
42445             this.el.on(this.autoHideHd);
42446         }
42447     },
42448
42449     clearAutoHide : function(){
42450         if(this.autoHide !== false){
42451             this.el.un("mouseout", this.autoHideHd.mouseout);
42452             this.el.un("mouseover", this.autoHideHd.mouseover);
42453         }
42454     },
42455
42456     clearMonitor : function(){
42457         Roo.get(document).un("click", this.slideInIf, this);
42458     },
42459
42460     // these names are backwards but not changed for compat
42461     slideOut : function(){
42462         if(this.isSlid || this.el.hasActiveFx()){
42463             return;
42464         }
42465         this.isSlid = true;
42466         if(this.collapseBtn){
42467             this.collapseBtn.hide();
42468         }
42469         this.closeBtnState = this.closeBtn.getStyle('display');
42470         this.closeBtn.hide();
42471         if(this.stickBtn){
42472             this.stickBtn.show();
42473         }
42474         this.el.show();
42475         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
42476         this.beforeSlide();
42477         this.el.setStyle("z-index", 10001);
42478         this.el.slideIn(this.getSlideAnchor(), {
42479             callback: function(){
42480                 this.afterSlide();
42481                 this.initAutoHide();
42482                 Roo.get(document).on("click", this.slideInIf, this);
42483                 this.fireEvent("slideshow", this);
42484             },
42485             scope: this,
42486             block: true
42487         });
42488     },
42489
42490     afterSlideIn : function(){
42491         this.clearAutoHide();
42492         this.isSlid = false;
42493         this.clearMonitor();
42494         this.el.setStyle("z-index", "");
42495         if(this.collapseBtn){
42496             this.collapseBtn.show();
42497         }
42498         this.closeBtn.setStyle('display', this.closeBtnState);
42499         if(this.stickBtn){
42500             this.stickBtn.hide();
42501         }
42502         this.fireEvent("slidehide", this);
42503     },
42504
42505     slideIn : function(cb){
42506         if(!this.isSlid || this.el.hasActiveFx()){
42507             Roo.callback(cb);
42508             return;
42509         }
42510         this.isSlid = false;
42511         this.beforeSlide();
42512         this.el.slideOut(this.getSlideAnchor(), {
42513             callback: function(){
42514                 this.el.setLeftTop(-10000, -10000);
42515                 this.afterSlide();
42516                 this.afterSlideIn();
42517                 Roo.callback(cb);
42518             },
42519             scope: this,
42520             block: true
42521         });
42522     },
42523     
42524     slideInIf : function(e){
42525         if(!e.within(this.el)){
42526             this.slideIn();
42527         }
42528     },
42529
42530     animateCollapse : function(){
42531         this.beforeSlide();
42532         this.el.setStyle("z-index", 20000);
42533         var anchor = this.getSlideAnchor();
42534         this.el.slideOut(anchor, {
42535             callback : function(){
42536                 this.el.setStyle("z-index", "");
42537                 this.collapsedEl.slideIn(anchor, {duration:.3});
42538                 this.afterSlide();
42539                 this.el.setLocation(-10000,-10000);
42540                 this.el.hide();
42541                 this.fireEvent("collapsed", this);
42542             },
42543             scope: this,
42544             block: true
42545         });
42546     },
42547
42548     animateExpand : function(){
42549         this.beforeSlide();
42550         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
42551         this.el.setStyle("z-index", 20000);
42552         this.collapsedEl.hide({
42553             duration:.1
42554         });
42555         this.el.slideIn(this.getSlideAnchor(), {
42556             callback : function(){
42557                 this.el.setStyle("z-index", "");
42558                 this.afterSlide();
42559                 if(this.split){
42560                     this.split.el.show();
42561                 }
42562                 this.fireEvent("invalidated", this);
42563                 this.fireEvent("expanded", this);
42564             },
42565             scope: this,
42566             block: true
42567         });
42568     },
42569
42570     anchors : {
42571         "west" : "left",
42572         "east" : "right",
42573         "north" : "top",
42574         "south" : "bottom"
42575     },
42576
42577     sanchors : {
42578         "west" : "l",
42579         "east" : "r",
42580         "north" : "t",
42581         "south" : "b"
42582     },
42583
42584     canchors : {
42585         "west" : "tl-tr",
42586         "east" : "tr-tl",
42587         "north" : "tl-bl",
42588         "south" : "bl-tl"
42589     },
42590
42591     getAnchor : function(){
42592         return this.anchors[this.position];
42593     },
42594
42595     getCollapseAnchor : function(){
42596         return this.canchors[this.position];
42597     },
42598
42599     getSlideAnchor : function(){
42600         return this.sanchors[this.position];
42601     },
42602
42603     getAlignAdj : function(){
42604         var cm = this.cmargins;
42605         switch(this.position){
42606             case "west":
42607                 return [0, 0];
42608             break;
42609             case "east":
42610                 return [0, 0];
42611             break;
42612             case "north":
42613                 return [0, 0];
42614             break;
42615             case "south":
42616                 return [0, 0];
42617             break;
42618         }
42619     },
42620
42621     getExpandAdj : function(){
42622         var c = this.collapsedEl, cm = this.cmargins;
42623         switch(this.position){
42624             case "west":
42625                 return [-(cm.right+c.getWidth()+cm.left), 0];
42626             break;
42627             case "east":
42628                 return [cm.right+c.getWidth()+cm.left, 0];
42629             break;
42630             case "north":
42631                 return [0, -(cm.top+cm.bottom+c.getHeight())];
42632             break;
42633             case "south":
42634                 return [0, cm.top+cm.bottom+c.getHeight()];
42635             break;
42636         }
42637     }
42638 });/*
42639  * Based on:
42640  * Ext JS Library 1.1.1
42641  * Copyright(c) 2006-2007, Ext JS, LLC.
42642  *
42643  * Originally Released Under LGPL - original licence link has changed is not relivant.
42644  *
42645  * Fork - LGPL
42646  * <script type="text/javascript">
42647  */
42648 /*
42649  * These classes are private internal classes
42650  */
42651 Roo.bootstrap.layout.Center = function(config){
42652     config.region = "center";
42653     Roo.bootstrap.layout.Region.call(this, config);
42654     this.visible = true;
42655     this.minWidth = config.minWidth || 20;
42656     this.minHeight = config.minHeight || 20;
42657 };
42658
42659 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
42660     hide : function(){
42661         // center panel can't be hidden
42662     },
42663     
42664     show : function(){
42665         // center panel can't be hidden
42666     },
42667     
42668     getMinWidth: function(){
42669         return this.minWidth;
42670     },
42671     
42672     getMinHeight: function(){
42673         return this.minHeight;
42674     }
42675 });
42676
42677
42678
42679
42680  
42681
42682
42683
42684
42685
42686
42687 Roo.bootstrap.layout.North = function(config)
42688 {
42689     config.region = 'north';
42690     config.cursor = 'n-resize';
42691     
42692     Roo.bootstrap.layout.Split.call(this, config);
42693     
42694     
42695     if(this.split){
42696         this.split.placement = Roo.bootstrap.SplitBar.TOP;
42697         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42698         this.split.el.addClass("roo-layout-split-v");
42699     }
42700     //var size = config.initialSize || config.height;
42701     //if(this.el && typeof size != "undefined"){
42702     //    this.el.setHeight(size);
42703     //}
42704 };
42705 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
42706 {
42707     orientation: Roo.bootstrap.SplitBar.VERTICAL,
42708      
42709      
42710     onRender : function(ctr, pos)
42711     {
42712         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42713         var size = this.config.initialSize || this.config.height;
42714         if(this.el && typeof size != "undefined"){
42715             this.el.setHeight(size);
42716         }
42717     
42718     },
42719     
42720     getBox : function(){
42721         if(this.collapsed){
42722             return this.collapsedEl.getBox();
42723         }
42724         var box = this.el.getBox();
42725         if(this.split){
42726             box.height += this.split.el.getHeight();
42727         }
42728         return box;
42729     },
42730     
42731     updateBox : function(box){
42732         if(this.split && !this.collapsed){
42733             box.height -= this.split.el.getHeight();
42734             this.split.el.setLeft(box.x);
42735             this.split.el.setTop(box.y+box.height);
42736             this.split.el.setWidth(box.width);
42737         }
42738         if(this.collapsed){
42739             this.updateBody(box.width, null);
42740         }
42741         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42742     }
42743 });
42744
42745
42746
42747
42748
42749 Roo.bootstrap.layout.South = function(config){
42750     config.region = 'south';
42751     config.cursor = 's-resize';
42752     Roo.bootstrap.layout.Split.call(this, config);
42753     if(this.split){
42754         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
42755         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42756         this.split.el.addClass("roo-layout-split-v");
42757     }
42758     
42759 };
42760
42761 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
42762     orientation: Roo.bootstrap.SplitBar.VERTICAL,
42763     
42764     onRender : function(ctr, pos)
42765     {
42766         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42767         var size = this.config.initialSize || this.config.height;
42768         if(this.el && typeof size != "undefined"){
42769             this.el.setHeight(size);
42770         }
42771     
42772     },
42773     
42774     getBox : function(){
42775         if(this.collapsed){
42776             return this.collapsedEl.getBox();
42777         }
42778         var box = this.el.getBox();
42779         if(this.split){
42780             var sh = this.split.el.getHeight();
42781             box.height += sh;
42782             box.y -= sh;
42783         }
42784         return box;
42785     },
42786     
42787     updateBox : function(box){
42788         if(this.split && !this.collapsed){
42789             var sh = this.split.el.getHeight();
42790             box.height -= sh;
42791             box.y += sh;
42792             this.split.el.setLeft(box.x);
42793             this.split.el.setTop(box.y-sh);
42794             this.split.el.setWidth(box.width);
42795         }
42796         if(this.collapsed){
42797             this.updateBody(box.width, null);
42798         }
42799         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42800     }
42801 });
42802
42803 Roo.bootstrap.layout.East = function(config){
42804     config.region = "east";
42805     config.cursor = "e-resize";
42806     Roo.bootstrap.layout.Split.call(this, config);
42807     if(this.split){
42808         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
42809         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
42810         this.split.el.addClass("roo-layout-split-h");
42811     }
42812     
42813 };
42814 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
42815     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
42816     
42817     onRender : function(ctr, pos)
42818     {
42819         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42820         var size = this.config.initialSize || this.config.width;
42821         if(this.el && typeof size != "undefined"){
42822             this.el.setWidth(size);
42823         }
42824     
42825     },
42826     
42827     getBox : function(){
42828         if(this.collapsed){
42829             return this.collapsedEl.getBox();
42830         }
42831         var box = this.el.getBox();
42832         if(this.split){
42833             var sw = this.split.el.getWidth();
42834             box.width += sw;
42835             box.x -= sw;
42836         }
42837         return box;
42838     },
42839
42840     updateBox : function(box){
42841         if(this.split && !this.collapsed){
42842             var sw = this.split.el.getWidth();
42843             box.width -= sw;
42844             this.split.el.setLeft(box.x);
42845             this.split.el.setTop(box.y);
42846             this.split.el.setHeight(box.height);
42847             box.x += sw;
42848         }
42849         if(this.collapsed){
42850             this.updateBody(null, box.height);
42851         }
42852         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42853     }
42854 });
42855
42856 Roo.bootstrap.layout.West = function(config){
42857     config.region = "west";
42858     config.cursor = "w-resize";
42859     
42860     Roo.bootstrap.layout.Split.call(this, config);
42861     if(this.split){
42862         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
42863         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
42864         this.split.el.addClass("roo-layout-split-h");
42865     }
42866     
42867 };
42868 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
42869     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
42870     
42871     onRender: function(ctr, pos)
42872     {
42873         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
42874         var size = this.config.initialSize || this.config.width;
42875         if(typeof size != "undefined"){
42876             this.el.setWidth(size);
42877         }
42878     },
42879     
42880     getBox : function(){
42881         if(this.collapsed){
42882             return this.collapsedEl.getBox();
42883         }
42884         var box = this.el.getBox();
42885         if (box.width == 0) {
42886             box.width = this.config.width; // kludge?
42887         }
42888         if(this.split){
42889             box.width += this.split.el.getWidth();
42890         }
42891         return box;
42892     },
42893     
42894     updateBox : function(box){
42895         if(this.split && !this.collapsed){
42896             var sw = this.split.el.getWidth();
42897             box.width -= sw;
42898             this.split.el.setLeft(box.x+box.width);
42899             this.split.el.setTop(box.y);
42900             this.split.el.setHeight(box.height);
42901         }
42902         if(this.collapsed){
42903             this.updateBody(null, box.height);
42904         }
42905         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42906     }
42907 });/*
42908  * Based on:
42909  * Ext JS Library 1.1.1
42910  * Copyright(c) 2006-2007, Ext JS, LLC.
42911  *
42912  * Originally Released Under LGPL - original licence link has changed is not relivant.
42913  *
42914  * Fork - LGPL
42915  * <script type="text/javascript">
42916  */
42917 /**
42918  * @class Roo.bootstrap.paenl.Content
42919  * @extends Roo.util.Observable
42920  * @children Roo.bootstrap.Component
42921  * @parent builder Roo.bootstrap.layout.Border
42922  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
42923  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
42924  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
42925  * @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
42926  * @cfg {Boolean}   closable      True if the panel can be closed/removed
42927  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
42928  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
42929  * @cfg {Toolbar}   toolbar       A toolbar for this panel
42930  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
42931  * @cfg {String} title          The title for this panel
42932  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
42933  * @cfg {String} url            Calls {@link #setUrl} with this value
42934  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
42935  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
42936  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
42937  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
42938  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
42939  * @cfg {Boolean} badges render the badges
42940  * @cfg {String} cls  extra classes to use  
42941  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
42942  
42943  * @constructor
42944  * Create a new ContentPanel.
42945  * @param {String/Object} config A string to set only the title or a config object
42946  
42947  */
42948 Roo.bootstrap.panel.Content = function( config){
42949     
42950     this.tpl = config.tpl || false;
42951     
42952     var el = config.el;
42953     var content = config.content;
42954
42955     if(config.autoCreate){ // xtype is available if this is called from factory
42956         el = Roo.id();
42957     }
42958     this.el = Roo.get(el);
42959     if(!this.el && config && config.autoCreate){
42960         if(typeof config.autoCreate == "object"){
42961             if(!config.autoCreate.id){
42962                 config.autoCreate.id = config.id||el;
42963             }
42964             this.el = Roo.DomHelper.append(document.body,
42965                         config.autoCreate, true);
42966         }else{
42967             var elcfg =  {
42968                 tag: "div",
42969                 cls: (config.cls || '') +
42970                     (config.background ? ' bg-' + config.background : '') +
42971                     " roo-layout-inactive-content",
42972                 id: config.id||el
42973             };
42974             if (config.iframe) {
42975                 elcfg.cn = [
42976                     {
42977                         tag : 'iframe',
42978                         style : 'border: 0px',
42979                         src : 'about:blank'
42980                     }
42981                 ];
42982             }
42983               
42984             if (config.html) {
42985                 elcfg.html = config.html;
42986                 
42987             }
42988                         
42989             this.el = Roo.DomHelper.append(document.body, elcfg , true);
42990             if (config.iframe) {
42991                 this.iframeEl = this.el.select('iframe',true).first();
42992             }
42993             
42994         }
42995     } 
42996     this.closable = false;
42997     this.loaded = false;
42998     this.active = false;
42999    
43000       
43001     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
43002         
43003         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
43004         
43005         this.wrapEl = this.el; //this.el.wrap();
43006         var ti = [];
43007         if (config.toolbar.items) {
43008             ti = config.toolbar.items ;
43009             delete config.toolbar.items ;
43010         }
43011         
43012         var nitems = [];
43013         this.toolbar.render(this.wrapEl, 'before');
43014         for(var i =0;i < ti.length;i++) {
43015           //  Roo.log(['add child', items[i]]);
43016             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43017         }
43018         this.toolbar.items = nitems;
43019         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
43020         delete config.toolbar;
43021         
43022     }
43023     /*
43024     // xtype created footer. - not sure if will work as we normally have to render first..
43025     if (this.footer && !this.footer.el && this.footer.xtype) {
43026         if (!this.wrapEl) {
43027             this.wrapEl = this.el.wrap();
43028         }
43029     
43030         this.footer.container = this.wrapEl.createChild();
43031          
43032         this.footer = Roo.factory(this.footer, Roo);
43033         
43034     }
43035     */
43036     
43037      if(typeof config == "string"){
43038         this.title = config;
43039     }else{
43040         Roo.apply(this, config);
43041     }
43042     
43043     if(this.resizeEl){
43044         this.resizeEl = Roo.get(this.resizeEl, true);
43045     }else{
43046         this.resizeEl = this.el;
43047     }
43048     // handle view.xtype
43049     
43050  
43051     
43052     
43053     this.addEvents({
43054         /**
43055          * @event activate
43056          * Fires when this panel is activated. 
43057          * @param {Roo.ContentPanel} this
43058          */
43059         "activate" : true,
43060         /**
43061          * @event deactivate
43062          * Fires when this panel is activated. 
43063          * @param {Roo.ContentPanel} this
43064          */
43065         "deactivate" : true,
43066
43067         /**
43068          * @event resize
43069          * Fires when this panel is resized if fitToFrame is true.
43070          * @param {Roo.ContentPanel} this
43071          * @param {Number} width The width after any component adjustments
43072          * @param {Number} height The height after any component adjustments
43073          */
43074         "resize" : true,
43075         
43076          /**
43077          * @event render
43078          * Fires when this tab is created
43079          * @param {Roo.ContentPanel} this
43080          */
43081         "render" : true,
43082         
43083           /**
43084          * @event scroll
43085          * Fires when this content is scrolled
43086          * @param {Roo.ContentPanel} this
43087          * @param {Event} scrollEvent
43088          */
43089         "scroll" : true
43090         
43091         
43092         
43093     });
43094     
43095
43096     
43097     
43098     if(this.autoScroll && !this.iframe){
43099         this.resizeEl.setStyle("overflow", "auto");
43100         this.resizeEl.on('scroll', this.onScroll, this);
43101     } else {
43102         // fix randome scrolling
43103         //this.el.on('scroll', function() {
43104         //    Roo.log('fix random scolling');
43105         //    this.scrollTo('top',0); 
43106         //});
43107     }
43108     content = content || this.content;
43109     if(content){
43110         this.setContent(content);
43111     }
43112     if(config && config.url){
43113         this.setUrl(this.url, this.params, this.loadOnce);
43114     }
43115     
43116     
43117     
43118     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
43119     
43120     if (this.view && typeof(this.view.xtype) != 'undefined') {
43121         this.view.el = this.el.appendChild(document.createElement("div"));
43122         this.view = Roo.factory(this.view); 
43123         this.view.render  &&  this.view.render(false, '');  
43124     }
43125     
43126     
43127     this.fireEvent('render', this);
43128 };
43129
43130 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
43131     
43132     cls : '',
43133     background : '',
43134     
43135     tabTip : '',
43136     
43137     iframe : false,
43138     iframeEl : false,
43139     
43140     /* Resize Element - use this to work out scroll etc. */
43141     resizeEl : false,
43142     
43143     setRegion : function(region){
43144         this.region = region;
43145         this.setActiveClass(region && !this.background);
43146     },
43147     
43148     
43149     setActiveClass: function(state)
43150     {
43151         if(state){
43152            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
43153            this.el.setStyle('position','relative');
43154         }else{
43155            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
43156            this.el.setStyle('position', 'absolute');
43157         } 
43158     },
43159     
43160     /**
43161      * Returns the toolbar for this Panel if one was configured. 
43162      * @return {Roo.Toolbar} 
43163      */
43164     getToolbar : function(){
43165         return this.toolbar;
43166     },
43167     
43168     setActiveState : function(active)
43169     {
43170         this.active = active;
43171         this.setActiveClass(active);
43172         if(!active){
43173             if(this.fireEvent("deactivate", this) === false){
43174                 return false;
43175             }
43176             return true;
43177         }
43178         this.fireEvent("activate", this);
43179         return true;
43180     },
43181     /**
43182      * Updates this panel's element (not for iframe)
43183      * @param {String} content The new content
43184      * @param {Boolean} loadScripts (optional) true to look for and process scripts
43185     */
43186     setContent : function(content, loadScripts){
43187         if (this.iframe) {
43188             return;
43189         }
43190         
43191         this.el.update(content, loadScripts);
43192     },
43193
43194     ignoreResize : function(w, h)
43195     {
43196         //return false; // always resize?
43197         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
43198             return true;
43199         }else{
43200             this.lastSize = {width: w, height: h};
43201             return false;
43202         }
43203     },
43204     /**
43205      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
43206      * @return {Roo.UpdateManager} The UpdateManager
43207      */
43208     getUpdateManager : function(){
43209         if (this.iframe) {
43210             return false;
43211         }
43212         return this.el.getUpdateManager();
43213     },
43214      /**
43215      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
43216      * Does not work with IFRAME contents
43217      * @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:
43218 <pre><code>
43219 panel.load({
43220     url: "your-url.php",
43221     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
43222     callback: yourFunction,
43223     scope: yourObject, //(optional scope)
43224     discardUrl: false,
43225     nocache: false,
43226     text: "Loading...",
43227     timeout: 30,
43228     scripts: false
43229 });
43230 </code></pre>
43231      
43232      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
43233      * 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.
43234      * @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}
43235      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
43236      * @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.
43237      * @return {Roo.ContentPanel} this
43238      */
43239     load : function(){
43240         
43241         if (this.iframe) {
43242             return this;
43243         }
43244         
43245         var um = this.el.getUpdateManager();
43246         um.update.apply(um, arguments);
43247         return this;
43248     },
43249
43250
43251     /**
43252      * 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.
43253      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
43254      * @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)
43255      * @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)
43256      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
43257      */
43258     setUrl : function(url, params, loadOnce){
43259         if (this.iframe) {
43260             this.iframeEl.dom.src = url;
43261             return false;
43262         }
43263         
43264         if(this.refreshDelegate){
43265             this.removeListener("activate", this.refreshDelegate);
43266         }
43267         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
43268         this.on("activate", this.refreshDelegate);
43269         return this.el.getUpdateManager();
43270     },
43271     
43272     _handleRefresh : function(url, params, loadOnce){
43273         if(!loadOnce || !this.loaded){
43274             var updater = this.el.getUpdateManager();
43275             updater.update(url, params, this._setLoaded.createDelegate(this));
43276         }
43277     },
43278     
43279     _setLoaded : function(){
43280         this.loaded = true;
43281     }, 
43282     
43283     /**
43284      * Returns this panel's id
43285      * @return {String} 
43286      */
43287     getId : function(){
43288         return this.el.id;
43289     },
43290     
43291     /** 
43292      * Returns this panel's element - used by regiosn to add.
43293      * @return {Roo.Element} 
43294      */
43295     getEl : function(){
43296         return this.wrapEl || this.el;
43297     },
43298     
43299    
43300     
43301     adjustForComponents : function(width, height)
43302     {
43303         //Roo.log('adjustForComponents ');
43304         if(this.resizeEl != this.el){
43305             width -= this.el.getFrameWidth('lr');
43306             height -= this.el.getFrameWidth('tb');
43307         }
43308         if(this.toolbar){
43309             var te = this.toolbar.getEl();
43310             te.setWidth(width);
43311             height -= te.getHeight();
43312         }
43313         if(this.footer){
43314             var te = this.footer.getEl();
43315             te.setWidth(width);
43316             height -= te.getHeight();
43317         }
43318         
43319         
43320         if(this.adjustments){
43321             width += this.adjustments[0];
43322             height += this.adjustments[1];
43323         }
43324         return {"width": width, "height": height};
43325     },
43326     
43327     setSize : function(width, height){
43328         if(this.fitToFrame && !this.ignoreResize(width, height)){
43329             if(this.fitContainer && this.resizeEl != this.el){
43330                 this.el.setSize(width, height);
43331             }
43332             var size = this.adjustForComponents(width, height);
43333             if (this.iframe) {
43334                 this.iframeEl.setSize(width,height);
43335             }
43336             
43337             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
43338             this.fireEvent('resize', this, size.width, size.height);
43339             
43340             
43341         }
43342     },
43343     
43344     /**
43345      * Returns this panel's title
43346      * @return {String} 
43347      */
43348     getTitle : function(){
43349         
43350         if (typeof(this.title) != 'object') {
43351             return this.title;
43352         }
43353         
43354         var t = '';
43355         for (var k in this.title) {
43356             if (!this.title.hasOwnProperty(k)) {
43357                 continue;
43358             }
43359             
43360             if (k.indexOf('-') >= 0) {
43361                 var s = k.split('-');
43362                 for (var i = 0; i<s.length; i++) {
43363                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
43364                 }
43365             } else {
43366                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
43367             }
43368         }
43369         return t;
43370     },
43371     
43372     /**
43373      * Set this panel's title
43374      * @param {String} title
43375      */
43376     setTitle : function(title){
43377         this.title = title;
43378         if(this.region){
43379             this.region.updatePanelTitle(this, title);
43380         }
43381     },
43382     
43383     /**
43384      * Returns true is this panel was configured to be closable
43385      * @return {Boolean} 
43386      */
43387     isClosable : function(){
43388         return this.closable;
43389     },
43390     
43391     beforeSlide : function(){
43392         this.el.clip();
43393         this.resizeEl.clip();
43394     },
43395     
43396     afterSlide : function(){
43397         this.el.unclip();
43398         this.resizeEl.unclip();
43399     },
43400     
43401     /**
43402      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
43403      *   Will fail silently if the {@link #setUrl} method has not been called.
43404      *   This does not activate the panel, just updates its content.
43405      */
43406     refresh : function(){
43407         if(this.refreshDelegate){
43408            this.loaded = false;
43409            this.refreshDelegate();
43410         }
43411     },
43412     
43413     /**
43414      * Destroys this panel
43415      */
43416     destroy : function(){
43417         this.el.removeAllListeners();
43418         var tempEl = document.createElement("span");
43419         tempEl.appendChild(this.el.dom);
43420         tempEl.innerHTML = "";
43421         this.el.remove();
43422         this.el = null;
43423     },
43424     
43425     /**
43426      * form - if the content panel contains a form - this is a reference to it.
43427      * @type {Roo.form.Form}
43428      */
43429     form : false,
43430     /**
43431      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
43432      *    This contains a reference to it.
43433      * @type {Roo.View}
43434      */
43435     view : false,
43436     
43437       /**
43438      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
43439      * <pre><code>
43440
43441 layout.addxtype({
43442        xtype : 'Form',
43443        items: [ .... ]
43444    }
43445 );
43446
43447 </code></pre>
43448      * @param {Object} cfg Xtype definition of item to add.
43449      */
43450     
43451     
43452     getChildContainer: function () {
43453         return this.getEl();
43454     },
43455     
43456     
43457     onScroll : function(e)
43458     {
43459         this.fireEvent('scroll', this, e);
43460     }
43461     
43462     
43463     /*
43464         var  ret = new Roo.factory(cfg);
43465         return ret;
43466         
43467         
43468         // add form..
43469         if (cfg.xtype.match(/^Form$/)) {
43470             
43471             var el;
43472             //if (this.footer) {
43473             //    el = this.footer.container.insertSibling(false, 'before');
43474             //} else {
43475                 el = this.el.createChild();
43476             //}
43477
43478             this.form = new  Roo.form.Form(cfg);
43479             
43480             
43481             if ( this.form.allItems.length) {
43482                 this.form.render(el.dom);
43483             }
43484             return this.form;
43485         }
43486         // should only have one of theses..
43487         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
43488             // views.. should not be just added - used named prop 'view''
43489             
43490             cfg.el = this.el.appendChild(document.createElement("div"));
43491             // factory?
43492             
43493             var ret = new Roo.factory(cfg);
43494              
43495              ret.render && ret.render(false, ''); // render blank..
43496             this.view = ret;
43497             return ret;
43498         }
43499         return false;
43500     }
43501     \*/
43502 });
43503  
43504 /**
43505  * @class Roo.bootstrap.panel.Grid
43506  * @extends Roo.bootstrap.panel.Content
43507  * @constructor
43508  * Create a new GridPanel.
43509  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
43510  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
43511  * @param {Object} config A the config object
43512   
43513  */
43514
43515
43516
43517 Roo.bootstrap.panel.Grid = function(config)
43518 {
43519     
43520       
43521     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
43522         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
43523
43524     config.el = this.wrapper;
43525     //this.el = this.wrapper;
43526     
43527       if (config.container) {
43528         // ctor'ed from a Border/panel.grid
43529         
43530         
43531         this.wrapper.setStyle("overflow", "hidden");
43532         this.wrapper.addClass('roo-grid-container');
43533
43534     }
43535     
43536     
43537     if(config.toolbar){
43538         var tool_el = this.wrapper.createChild();    
43539         this.toolbar = Roo.factory(config.toolbar);
43540         var ti = [];
43541         if (config.toolbar.items) {
43542             ti = config.toolbar.items ;
43543             delete config.toolbar.items ;
43544         }
43545         
43546         var nitems = [];
43547         this.toolbar.render(tool_el);
43548         for(var i =0;i < ti.length;i++) {
43549           //  Roo.log(['add child', items[i]]);
43550             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43551         }
43552         this.toolbar.items = nitems;
43553         
43554         delete config.toolbar;
43555     }
43556     
43557     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
43558     config.grid.scrollBody = true;;
43559     config.grid.monitorWindowResize = false; // turn off autosizing
43560     config.grid.autoHeight = false;
43561     config.grid.autoWidth = false;
43562     
43563     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
43564     
43565     if (config.background) {
43566         // render grid on panel activation (if panel background)
43567         this.on('activate', function(gp) {
43568             if (!gp.grid.rendered) {
43569                 gp.grid.render(this.wrapper);
43570                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
43571             }
43572         });
43573             
43574     } else {
43575         this.grid.render(this.wrapper);
43576         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
43577
43578     }
43579     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
43580     // ??? needed ??? config.el = this.wrapper;
43581     
43582     
43583     
43584   
43585     // xtype created footer. - not sure if will work as we normally have to render first..
43586     if (this.footer && !this.footer.el && this.footer.xtype) {
43587         
43588         var ctr = this.grid.getView().getFooterPanel(true);
43589         this.footer.dataSource = this.grid.dataSource;
43590         this.footer = Roo.factory(this.footer, Roo);
43591         this.footer.render(ctr);
43592         
43593     }
43594     
43595     
43596     
43597     
43598      
43599 };
43600
43601 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
43602 {
43603   
43604     getId : function(){
43605         return this.grid.id;
43606     },
43607     
43608     /**
43609      * Returns the grid for this panel
43610      * @return {Roo.bootstrap.Table} 
43611      */
43612     getGrid : function(){
43613         return this.grid;    
43614     },
43615     
43616     setSize : function(width, height)
43617     {
43618      
43619         //if(!this.ignoreResize(width, height)){
43620             var grid = this.grid;
43621             var size = this.adjustForComponents(width, height);
43622             // tfoot is not a footer?
43623           
43624             
43625             var gridel = grid.getGridEl();
43626             gridel.setSize(size.width, size.height);
43627             
43628             var tbd = grid.getGridEl().select('tbody', true).first();
43629             var thd = grid.getGridEl().select('thead',true).first();
43630             var tbf= grid.getGridEl().select('tfoot', true).first();
43631
43632             if (tbf) {
43633                 size.height -= tbf.getHeight();
43634             }
43635             if (thd) {
43636                 size.height -= thd.getHeight();
43637             }
43638             
43639             tbd.setSize(size.width, size.height );
43640             // this is for the account management tab -seems to work there.
43641             var thd = grid.getGridEl().select('thead',true).first();
43642             //if (tbd) {
43643             //    tbd.setSize(size.width, size.height - thd.getHeight());
43644             //}
43645              
43646             grid.autoSize();
43647         //}
43648    
43649     },
43650      
43651     
43652     
43653     beforeSlide : function(){
43654         this.grid.getView().scroller.clip();
43655     },
43656     
43657     afterSlide : function(){
43658         this.grid.getView().scroller.unclip();
43659     },
43660     
43661     destroy : function(){
43662         this.grid.destroy();
43663         delete this.grid;
43664         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
43665     }
43666 });
43667
43668 /**
43669  * @class Roo.bootstrap.panel.Nest
43670  * @extends Roo.bootstrap.panel.Content
43671  * @constructor
43672  * Create a new Panel, that can contain a layout.Border.
43673  * 
43674  * 
43675  * @param {String/Object} config A string to set only the title or a config object
43676  */
43677 Roo.bootstrap.panel.Nest = function(config)
43678 {
43679     // construct with only one argument..
43680     /* FIXME - implement nicer consturctors
43681     if (layout.layout) {
43682         config = layout;
43683         layout = config.layout;
43684         delete config.layout;
43685     }
43686     if (layout.xtype && !layout.getEl) {
43687         // then layout needs constructing..
43688         layout = Roo.factory(layout, Roo);
43689     }
43690     */
43691     
43692     config.el =  config.layout.getEl();
43693     
43694     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
43695     
43696     config.layout.monitorWindowResize = false; // turn off autosizing
43697     this.layout = config.layout;
43698     this.layout.getEl().addClass("roo-layout-nested-layout");
43699     this.layout.parent = this;
43700     
43701     
43702     
43703     
43704 };
43705
43706 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
43707     /**
43708     * @cfg {Roo.BorderLayout} layout The layout for this panel
43709     */
43710     layout : false,
43711
43712     setSize : function(width, height){
43713         if(!this.ignoreResize(width, height)){
43714             var size = this.adjustForComponents(width, height);
43715             var el = this.layout.getEl();
43716             if (size.height < 1) {
43717                 el.setWidth(size.width);   
43718             } else {
43719                 el.setSize(size.width, size.height);
43720             }
43721             var touch = el.dom.offsetWidth;
43722             this.layout.layout();
43723             // ie requires a double layout on the first pass
43724             if(Roo.isIE && !this.initialized){
43725                 this.initialized = true;
43726                 this.layout.layout();
43727             }
43728         }
43729     },
43730     
43731     // activate all subpanels if not currently active..
43732     
43733     setActiveState : function(active){
43734         this.active = active;
43735         this.setActiveClass(active);
43736         
43737         if(!active){
43738             this.fireEvent("deactivate", this);
43739             return;
43740         }
43741         
43742         this.fireEvent("activate", this);
43743         // not sure if this should happen before or after..
43744         if (!this.layout) {
43745             return; // should not happen..
43746         }
43747         var reg = false;
43748         for (var r in this.layout.regions) {
43749             reg = this.layout.getRegion(r);
43750             if (reg.getActivePanel()) {
43751                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
43752                 reg.setActivePanel(reg.getActivePanel());
43753                 continue;
43754             }
43755             if (!reg.panels.length) {
43756                 continue;
43757             }
43758             reg.showPanel(reg.getPanel(0));
43759         }
43760         
43761         
43762         
43763         
43764     },
43765     
43766     /**
43767      * Returns the nested BorderLayout for this panel
43768      * @return {Roo.BorderLayout} 
43769      */
43770     getLayout : function(){
43771         return this.layout;
43772     },
43773     
43774      /**
43775      * Adds a xtype elements to the layout of the nested panel
43776      * <pre><code>
43777
43778 panel.addxtype({
43779        xtype : 'ContentPanel',
43780        region: 'west',
43781        items: [ .... ]
43782    }
43783 );
43784
43785 panel.addxtype({
43786         xtype : 'NestedLayoutPanel',
43787         region: 'west',
43788         layout: {
43789            center: { },
43790            west: { }   
43791         },
43792         items : [ ... list of content panels or nested layout panels.. ]
43793    }
43794 );
43795 </code></pre>
43796      * @param {Object} cfg Xtype definition of item to add.
43797      */
43798     addxtype : function(cfg) {
43799         return this.layout.addxtype(cfg);
43800     
43801     }
43802 });/*
43803  * Based on:
43804  * Ext JS Library 1.1.1
43805  * Copyright(c) 2006-2007, Ext JS, LLC.
43806  *
43807  * Originally Released Under LGPL - original licence link has changed is not relivant.
43808  *
43809  * Fork - LGPL
43810  * <script type="text/javascript">
43811  */
43812 /**
43813  * @class Roo.TabPanel
43814  * @extends Roo.util.Observable
43815  * A lightweight tab container.
43816  * <br><br>
43817  * Usage:
43818  * <pre><code>
43819 // basic tabs 1, built from existing content
43820 var tabs = new Roo.TabPanel("tabs1");
43821 tabs.addTab("script", "View Script");
43822 tabs.addTab("markup", "View Markup");
43823 tabs.activate("script");
43824
43825 // more advanced tabs, built from javascript
43826 var jtabs = new Roo.TabPanel("jtabs");
43827 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
43828
43829 // set up the UpdateManager
43830 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
43831 var updater = tab2.getUpdateManager();
43832 updater.setDefaultUrl("ajax1.htm");
43833 tab2.on('activate', updater.refresh, updater, true);
43834
43835 // Use setUrl for Ajax loading
43836 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
43837 tab3.setUrl("ajax2.htm", null, true);
43838
43839 // Disabled tab
43840 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
43841 tab4.disable();
43842
43843 jtabs.activate("jtabs-1");
43844  * </code></pre>
43845  * @constructor
43846  * Create a new TabPanel.
43847  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
43848  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
43849  */
43850 Roo.bootstrap.panel.Tabs = function(config){
43851     /**
43852     * The container element for this TabPanel.
43853     * @type Roo.Element
43854     */
43855     this.el = Roo.get(config.el);
43856     delete config.el;
43857     if(config){
43858         if(typeof config == "boolean"){
43859             this.tabPosition = config ? "bottom" : "top";
43860         }else{
43861             Roo.apply(this, config);
43862         }
43863     }
43864     
43865     if(this.tabPosition == "bottom"){
43866         // if tabs are at the bottom = create the body first.
43867         this.bodyEl = Roo.get(this.createBody(this.el.dom));
43868         this.el.addClass("roo-tabs-bottom");
43869     }
43870     // next create the tabs holders
43871     
43872     if (this.tabPosition == "west"){
43873         
43874         var reg = this.region; // fake it..
43875         while (reg) {
43876             if (!reg.mgr.parent) {
43877                 break;
43878             }
43879             reg = reg.mgr.parent.region;
43880         }
43881         Roo.log("got nest?");
43882         Roo.log(reg);
43883         if (reg.mgr.getRegion('west')) {
43884             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
43885             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
43886             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
43887             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
43888             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
43889         
43890             
43891         }
43892         
43893         
43894     } else {
43895      
43896         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
43897         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
43898         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
43899         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
43900     }
43901     
43902     
43903     if(Roo.isIE){
43904         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
43905     }
43906     
43907     // finally - if tabs are at the top, then create the body last..
43908     if(this.tabPosition != "bottom"){
43909         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
43910          * @type Roo.Element
43911          */
43912         this.bodyEl = Roo.get(this.createBody(this.el.dom));
43913         this.el.addClass("roo-tabs-top");
43914     }
43915     this.items = [];
43916
43917     this.bodyEl.setStyle("position", "relative");
43918
43919     this.active = null;
43920     this.activateDelegate = this.activate.createDelegate(this);
43921
43922     this.addEvents({
43923         /**
43924          * @event tabchange
43925          * Fires when the active tab changes
43926          * @param {Roo.TabPanel} this
43927          * @param {Roo.TabPanelItem} activePanel The new active tab
43928          */
43929         "tabchange": true,
43930         /**
43931          * @event beforetabchange
43932          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
43933          * @param {Roo.TabPanel} this
43934          * @param {Object} e Set cancel to true on this object to cancel the tab change
43935          * @param {Roo.TabPanelItem} tab The tab being changed to
43936          */
43937         "beforetabchange" : true
43938     });
43939
43940     Roo.EventManager.onWindowResize(this.onResize, this);
43941     this.cpad = this.el.getPadding("lr");
43942     this.hiddenCount = 0;
43943
43944
43945     // toolbar on the tabbar support...
43946     if (this.toolbar) {
43947         alert("no toolbar support yet");
43948         this.toolbar  = false;
43949         /*
43950         var tcfg = this.toolbar;
43951         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
43952         this.toolbar = new Roo.Toolbar(tcfg);
43953         if (Roo.isSafari) {
43954             var tbl = tcfg.container.child('table', true);
43955             tbl.setAttribute('width', '100%');
43956         }
43957         */
43958         
43959     }
43960    
43961
43962
43963     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
43964 };
43965
43966 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
43967     /*
43968      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
43969      */
43970     tabPosition : "top",
43971     /*
43972      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
43973      */
43974     currentTabWidth : 0,
43975     /*
43976      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
43977      */
43978     minTabWidth : 40,
43979     /*
43980      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
43981      */
43982     maxTabWidth : 250,
43983     /*
43984      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
43985      */
43986     preferredTabWidth : 175,
43987     /*
43988      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
43989      */
43990     resizeTabs : false,
43991     /*
43992      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
43993      */
43994     monitorResize : true,
43995     /*
43996      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
43997      */
43998     toolbar : false,  // set by caller..
43999     
44000     region : false, /// set by caller
44001     
44002     disableTooltips : true, // not used yet...
44003
44004     /**
44005      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
44006      * @param {String} id The id of the div to use <b>or create</b>
44007      * @param {String} text The text for the tab
44008      * @param {String} content (optional) Content to put in the TabPanelItem body
44009      * @param {Boolean} closable (optional) True to create a close icon on the tab
44010      * @return {Roo.TabPanelItem} The created TabPanelItem
44011      */
44012     addTab : function(id, text, content, closable, tpl)
44013     {
44014         var item = new Roo.bootstrap.panel.TabItem({
44015             panel: this,
44016             id : id,
44017             text : text,
44018             closable : closable,
44019             tpl : tpl
44020         });
44021         this.addTabItem(item);
44022         if(content){
44023             item.setContent(content);
44024         }
44025         return item;
44026     },
44027
44028     /**
44029      * Returns the {@link Roo.TabPanelItem} with the specified id/index
44030      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
44031      * @return {Roo.TabPanelItem}
44032      */
44033     getTab : function(id){
44034         return this.items[id];
44035     },
44036
44037     /**
44038      * Hides the {@link Roo.TabPanelItem} with the specified id/index
44039      * @param {String/Number} id The id or index of the TabPanelItem to hide.
44040      */
44041     hideTab : function(id){
44042         var t = this.items[id];
44043         if(!t.isHidden()){
44044            t.setHidden(true);
44045            this.hiddenCount++;
44046            this.autoSizeTabs();
44047         }
44048     },
44049
44050     /**
44051      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
44052      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
44053      */
44054     unhideTab : function(id){
44055         var t = this.items[id];
44056         if(t.isHidden()){
44057            t.setHidden(false);
44058            this.hiddenCount--;
44059            this.autoSizeTabs();
44060         }
44061     },
44062
44063     /**
44064      * Adds an existing {@link Roo.TabPanelItem}.
44065      * @param {Roo.TabPanelItem} item The TabPanelItem to add
44066      */
44067     addTabItem : function(item)
44068     {
44069         this.items[item.id] = item;
44070         this.items.push(item);
44071         this.autoSizeTabs();
44072       //  if(this.resizeTabs){
44073     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
44074   //         this.autoSizeTabs();
44075 //        }else{
44076 //            item.autoSize();
44077        // }
44078     },
44079
44080     /**
44081      * Removes a {@link Roo.TabPanelItem}.
44082      * @param {String/Number} id The id or index of the TabPanelItem to remove.
44083      */
44084     removeTab : function(id){
44085         var items = this.items;
44086         var tab = items[id];
44087         if(!tab) { return; }
44088         var index = items.indexOf(tab);
44089         if(this.active == tab && items.length > 1){
44090             var newTab = this.getNextAvailable(index);
44091             if(newTab) {
44092                 newTab.activate();
44093             }
44094         }
44095         this.stripEl.dom.removeChild(tab.pnode.dom);
44096         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
44097             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
44098         }
44099         items.splice(index, 1);
44100         delete this.items[tab.id];
44101         tab.fireEvent("close", tab);
44102         tab.purgeListeners();
44103         this.autoSizeTabs();
44104     },
44105
44106     getNextAvailable : function(start){
44107         var items = this.items;
44108         var index = start;
44109         // look for a next tab that will slide over to
44110         // replace the one being removed
44111         while(index < items.length){
44112             var item = items[++index];
44113             if(item && !item.isHidden()){
44114                 return item;
44115             }
44116         }
44117         // if one isn't found select the previous tab (on the left)
44118         index = start;
44119         while(index >= 0){
44120             var item = items[--index];
44121             if(item && !item.isHidden()){
44122                 return item;
44123             }
44124         }
44125         return null;
44126     },
44127
44128     /**
44129      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
44130      * @param {String/Number} id The id or index of the TabPanelItem to disable.
44131      */
44132     disableTab : function(id){
44133         var tab = this.items[id];
44134         if(tab && this.active != tab){
44135             tab.disable();
44136         }
44137     },
44138
44139     /**
44140      * Enables a {@link Roo.TabPanelItem} that is disabled.
44141      * @param {String/Number} id The id or index of the TabPanelItem to enable.
44142      */
44143     enableTab : function(id){
44144         var tab = this.items[id];
44145         tab.enable();
44146     },
44147
44148     /**
44149      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
44150      * @param {String/Number} id The id or index of the TabPanelItem to activate.
44151      * @return {Roo.TabPanelItem} The TabPanelItem.
44152      */
44153     activate : function(id)
44154     {
44155         //Roo.log('activite:'  + id);
44156         
44157         var tab = this.items[id];
44158         if(!tab){
44159             return null;
44160         }
44161         if(tab == this.active || tab.disabled){
44162             return tab;
44163         }
44164         var e = {};
44165         this.fireEvent("beforetabchange", this, e, tab);
44166         if(e.cancel !== true && !tab.disabled){
44167             if(this.active){
44168                 this.active.hide();
44169             }
44170             this.active = this.items[id];
44171             this.active.show();
44172             this.fireEvent("tabchange", this, this.active);
44173         }
44174         return tab;
44175     },
44176
44177     /**
44178      * Gets the active {@link Roo.TabPanelItem}.
44179      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
44180      */
44181     getActiveTab : function(){
44182         return this.active;
44183     },
44184
44185     /**
44186      * Updates the tab body element to fit the height of the container element
44187      * for overflow scrolling
44188      * @param {Number} targetHeight (optional) Override the starting height from the elements height
44189      */
44190     syncHeight : function(targetHeight){
44191         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44192         var bm = this.bodyEl.getMargins();
44193         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
44194         this.bodyEl.setHeight(newHeight);
44195         return newHeight;
44196     },
44197
44198     onResize : function(){
44199         if(this.monitorResize){
44200             this.autoSizeTabs();
44201         }
44202     },
44203
44204     /**
44205      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
44206      */
44207     beginUpdate : function(){
44208         this.updating = true;
44209     },
44210
44211     /**
44212      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
44213      */
44214     endUpdate : function(){
44215         this.updating = false;
44216         this.autoSizeTabs();
44217     },
44218
44219     /**
44220      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
44221      */
44222     autoSizeTabs : function()
44223     {
44224         var count = this.items.length;
44225         var vcount = count - this.hiddenCount;
44226         
44227         if (vcount < 2) {
44228             this.stripEl.hide();
44229         } else {
44230             this.stripEl.show();
44231         }
44232         
44233         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
44234             return;
44235         }
44236         
44237         
44238         var w = Math.max(this.el.getWidth() - this.cpad, 10);
44239         var availWidth = Math.floor(w / vcount);
44240         var b = this.stripBody;
44241         if(b.getWidth() > w){
44242             var tabs = this.items;
44243             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
44244             if(availWidth < this.minTabWidth){
44245                 /*if(!this.sleft){    // incomplete scrolling code
44246                     this.createScrollButtons();
44247                 }
44248                 this.showScroll();
44249                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
44250             }
44251         }else{
44252             if(this.currentTabWidth < this.preferredTabWidth){
44253                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
44254             }
44255         }
44256     },
44257
44258     /**
44259      * Returns the number of tabs in this TabPanel.
44260      * @return {Number}
44261      */
44262      getCount : function(){
44263          return this.items.length;
44264      },
44265
44266     /**
44267      * Resizes all the tabs to the passed width
44268      * @param {Number} The new width
44269      */
44270     setTabWidth : function(width){
44271         this.currentTabWidth = width;
44272         for(var i = 0, len = this.items.length; i < len; i++) {
44273                 if(!this.items[i].isHidden()) {
44274                 this.items[i].setWidth(width);
44275             }
44276         }
44277     },
44278
44279     /**
44280      * Destroys this TabPanel
44281      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
44282      */
44283     destroy : function(removeEl){
44284         Roo.EventManager.removeResizeListener(this.onResize, this);
44285         for(var i = 0, len = this.items.length; i < len; i++){
44286             this.items[i].purgeListeners();
44287         }
44288         if(removeEl === true){
44289             this.el.update("");
44290             this.el.remove();
44291         }
44292     },
44293     
44294     createStrip : function(container)
44295     {
44296         var strip = document.createElement("nav");
44297         strip.className = Roo.bootstrap.version == 4 ?
44298             "navbar-light bg-light" : 
44299             "navbar navbar-default"; //"x-tabs-wrap";
44300         container.appendChild(strip);
44301         return strip;
44302     },
44303     
44304     createStripList : function(strip)
44305     {
44306         // div wrapper for retard IE
44307         // returns the "tr" element.
44308         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
44309         //'<div class="x-tabs-strip-wrap">'+
44310           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
44311           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
44312         return strip.firstChild; //.firstChild.firstChild.firstChild;
44313     },
44314     createBody : function(container)
44315     {
44316         var body = document.createElement("div");
44317         Roo.id(body, "tab-body");
44318         //Roo.fly(body).addClass("x-tabs-body");
44319         Roo.fly(body).addClass("tab-content");
44320         container.appendChild(body);
44321         return body;
44322     },
44323     createItemBody :function(bodyEl, id){
44324         var body = Roo.getDom(id);
44325         if(!body){
44326             body = document.createElement("div");
44327             body.id = id;
44328         }
44329         //Roo.fly(body).addClass("x-tabs-item-body");
44330         Roo.fly(body).addClass("tab-pane");
44331          bodyEl.insertBefore(body, bodyEl.firstChild);
44332         return body;
44333     },
44334     /** @private */
44335     createStripElements :  function(stripEl, text, closable, tpl)
44336     {
44337         var td = document.createElement("li"); // was td..
44338         td.className = 'nav-item';
44339         
44340         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
44341         
44342         
44343         stripEl.appendChild(td);
44344         /*if(closable){
44345             td.className = "x-tabs-closable";
44346             if(!this.closeTpl){
44347                 this.closeTpl = new Roo.Template(
44348                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44349                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
44350                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
44351                 );
44352             }
44353             var el = this.closeTpl.overwrite(td, {"text": text});
44354             var close = el.getElementsByTagName("div")[0];
44355             var inner = el.getElementsByTagName("em")[0];
44356             return {"el": el, "close": close, "inner": inner};
44357         } else {
44358         */
44359         // not sure what this is..
44360 //            if(!this.tabTpl){
44361                 //this.tabTpl = new Roo.Template(
44362                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44363                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
44364                 //);
44365 //                this.tabTpl = new Roo.Template(
44366 //                   '<a href="#">' +
44367 //                   '<span unselectable="on"' +
44368 //                            (this.disableTooltips ? '' : ' title="{text}"') +
44369 //                            ' >{text}</span></a>'
44370 //                );
44371 //                
44372 //            }
44373
44374
44375             var template = tpl || this.tabTpl || false;
44376             
44377             if(!template){
44378                 template =  new Roo.Template(
44379                         Roo.bootstrap.version == 4 ? 
44380                             (
44381                                 '<a class="nav-link" href="#" unselectable="on"' +
44382                                      (this.disableTooltips ? '' : ' title="{text}"') +
44383                                      ' >{text}</a>'
44384                             ) : (
44385                                 '<a class="nav-link" href="#">' +
44386                                 '<span unselectable="on"' +
44387                                          (this.disableTooltips ? '' : ' title="{text}"') +
44388                                     ' >{text}</span></a>'
44389                             )
44390                 );
44391             }
44392             
44393             switch (typeof(template)) {
44394                 case 'object' :
44395                     break;
44396                 case 'string' :
44397                     template = new Roo.Template(template);
44398                     break;
44399                 default :
44400                     break;
44401             }
44402             
44403             var el = template.overwrite(td, {"text": text});
44404             
44405             var inner = el.getElementsByTagName("span")[0];
44406             
44407             return {"el": el, "inner": inner};
44408             
44409     }
44410         
44411     
44412 });
44413
44414 /**
44415  * @class Roo.TabPanelItem
44416  * @extends Roo.util.Observable
44417  * Represents an individual item (tab plus body) in a TabPanel.
44418  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
44419  * @param {String} id The id of this TabPanelItem
44420  * @param {String} text The text for the tab of this TabPanelItem
44421  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
44422  */
44423 Roo.bootstrap.panel.TabItem = function(config){
44424     /**
44425      * The {@link Roo.TabPanel} this TabPanelItem belongs to
44426      * @type Roo.TabPanel
44427      */
44428     this.tabPanel = config.panel;
44429     /**
44430      * The id for this TabPanelItem
44431      * @type String
44432      */
44433     this.id = config.id;
44434     /** @private */
44435     this.disabled = false;
44436     /** @private */
44437     this.text = config.text;
44438     /** @private */
44439     this.loaded = false;
44440     this.closable = config.closable;
44441
44442     /**
44443      * The body element for this TabPanelItem.
44444      * @type Roo.Element
44445      */
44446     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
44447     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
44448     this.bodyEl.setStyle("display", "block");
44449     this.bodyEl.setStyle("zoom", "1");
44450     //this.hideAction();
44451
44452     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
44453     /** @private */
44454     this.el = Roo.get(els.el);
44455     this.inner = Roo.get(els.inner, true);
44456      this.textEl = Roo.bootstrap.version == 4 ?
44457         this.el : Roo.get(this.el.dom.firstChild, true);
44458
44459     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
44460     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
44461
44462     
44463 //    this.el.on("mousedown", this.onTabMouseDown, this);
44464     this.el.on("click", this.onTabClick, this);
44465     /** @private */
44466     if(config.closable){
44467         var c = Roo.get(els.close, true);
44468         c.dom.title = this.closeText;
44469         c.addClassOnOver("close-over");
44470         c.on("click", this.closeClick, this);
44471      }
44472
44473     this.addEvents({
44474          /**
44475          * @event activate
44476          * Fires when this tab becomes the active tab.
44477          * @param {Roo.TabPanel} tabPanel The parent TabPanel
44478          * @param {Roo.TabPanelItem} this
44479          */
44480         "activate": true,
44481         /**
44482          * @event beforeclose
44483          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
44484          * @param {Roo.TabPanelItem} this
44485          * @param {Object} e Set cancel to true on this object to cancel the close.
44486          */
44487         "beforeclose": true,
44488         /**
44489          * @event close
44490          * Fires when this tab is closed.
44491          * @param {Roo.TabPanelItem} this
44492          */
44493          "close": true,
44494         /**
44495          * @event deactivate
44496          * Fires when this tab is no longer the active tab.
44497          * @param {Roo.TabPanel} tabPanel The parent TabPanel
44498          * @param {Roo.TabPanelItem} this
44499          */
44500          "deactivate" : true
44501     });
44502     this.hidden = false;
44503
44504     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
44505 };
44506
44507 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
44508            {
44509     purgeListeners : function(){
44510        Roo.util.Observable.prototype.purgeListeners.call(this);
44511        this.el.removeAllListeners();
44512     },
44513     /**
44514      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
44515      */
44516     show : function(){
44517         this.status_node.addClass("active");
44518         this.showAction();
44519         if(Roo.isOpera){
44520             this.tabPanel.stripWrap.repaint();
44521         }
44522         this.fireEvent("activate", this.tabPanel, this);
44523     },
44524
44525     /**
44526      * Returns true if this tab is the active tab.
44527      * @return {Boolean}
44528      */
44529     isActive : function(){
44530         return this.tabPanel.getActiveTab() == this;
44531     },
44532
44533     /**
44534      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
44535      */
44536     hide : function(){
44537         this.status_node.removeClass("active");
44538         this.hideAction();
44539         this.fireEvent("deactivate", this.tabPanel, this);
44540     },
44541
44542     hideAction : function(){
44543         this.bodyEl.hide();
44544         this.bodyEl.setStyle("position", "absolute");
44545         this.bodyEl.setLeft("-20000px");
44546         this.bodyEl.setTop("-20000px");
44547     },
44548
44549     showAction : function(){
44550         this.bodyEl.setStyle("position", "relative");
44551         this.bodyEl.setTop("");
44552         this.bodyEl.setLeft("");
44553         this.bodyEl.show();
44554     },
44555
44556     /**
44557      * Set the tooltip for the tab.
44558      * @param {String} tooltip The tab's tooltip
44559      */
44560     setTooltip : function(text){
44561         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
44562             this.textEl.dom.qtip = text;
44563             this.textEl.dom.removeAttribute('title');
44564         }else{
44565             this.textEl.dom.title = text;
44566         }
44567     },
44568
44569     onTabClick : function(e){
44570         e.preventDefault();
44571         this.tabPanel.activate(this.id);
44572     },
44573
44574     onTabMouseDown : function(e){
44575         e.preventDefault();
44576         this.tabPanel.activate(this.id);
44577     },
44578 /*
44579     getWidth : function(){
44580         return this.inner.getWidth();
44581     },
44582
44583     setWidth : function(width){
44584         var iwidth = width - this.linode.getPadding("lr");
44585         this.inner.setWidth(iwidth);
44586         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
44587         this.linode.setWidth(width);
44588     },
44589 */
44590     /**
44591      * Show or hide the tab
44592      * @param {Boolean} hidden True to hide or false to show.
44593      */
44594     setHidden : function(hidden){
44595         this.hidden = hidden;
44596         this.linode.setStyle("display", hidden ? "none" : "");
44597     },
44598
44599     /**
44600      * Returns true if this tab is "hidden"
44601      * @return {Boolean}
44602      */
44603     isHidden : function(){
44604         return this.hidden;
44605     },
44606
44607     /**
44608      * Returns the text for this tab
44609      * @return {String}
44610      */
44611     getText : function(){
44612         return this.text;
44613     },
44614     /*
44615     autoSize : function(){
44616         //this.el.beginMeasure();
44617         this.textEl.setWidth(1);
44618         /*
44619          *  #2804 [new] Tabs in Roojs
44620          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
44621          */
44622         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
44623         //this.el.endMeasure();
44624     //},
44625
44626     /**
44627      * Sets the text for the tab (Note: this also sets the tooltip text)
44628      * @param {String} text The tab's text and tooltip
44629      */
44630     setText : function(text){
44631         this.text = text;
44632         this.textEl.update(text);
44633         this.setTooltip(text);
44634         //if(!this.tabPanel.resizeTabs){
44635         //    this.autoSize();
44636         //}
44637     },
44638     /**
44639      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
44640      */
44641     activate : function(){
44642         this.tabPanel.activate(this.id);
44643     },
44644
44645     /**
44646      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
44647      */
44648     disable : function(){
44649         if(this.tabPanel.active != this){
44650             this.disabled = true;
44651             this.status_node.addClass("disabled");
44652         }
44653     },
44654
44655     /**
44656      * Enables this TabPanelItem if it was previously disabled.
44657      */
44658     enable : function(){
44659         this.disabled = false;
44660         this.status_node.removeClass("disabled");
44661     },
44662
44663     /**
44664      * Sets the content for this TabPanelItem.
44665      * @param {String} content The content
44666      * @param {Boolean} loadScripts true to look for and load scripts
44667      */
44668     setContent : function(content, loadScripts){
44669         this.bodyEl.update(content, loadScripts);
44670     },
44671
44672     /**
44673      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
44674      * @return {Roo.UpdateManager} The UpdateManager
44675      */
44676     getUpdateManager : function(){
44677         return this.bodyEl.getUpdateManager();
44678     },
44679
44680     /**
44681      * Set a URL to be used to load the content for this TabPanelItem.
44682      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
44683      * @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)
44684      * @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)
44685      * @return {Roo.UpdateManager} The UpdateManager
44686      */
44687     setUrl : function(url, params, loadOnce){
44688         if(this.refreshDelegate){
44689             this.un('activate', this.refreshDelegate);
44690         }
44691         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44692         this.on("activate", this.refreshDelegate);
44693         return this.bodyEl.getUpdateManager();
44694     },
44695
44696     /** @private */
44697     _handleRefresh : function(url, params, loadOnce){
44698         if(!loadOnce || !this.loaded){
44699             var updater = this.bodyEl.getUpdateManager();
44700             updater.update(url, params, this._setLoaded.createDelegate(this));
44701         }
44702     },
44703
44704     /**
44705      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
44706      *   Will fail silently if the setUrl method has not been called.
44707      *   This does not activate the panel, just updates its content.
44708      */
44709     refresh : function(){
44710         if(this.refreshDelegate){
44711            this.loaded = false;
44712            this.refreshDelegate();
44713         }
44714     },
44715
44716     /** @private */
44717     _setLoaded : function(){
44718         this.loaded = true;
44719     },
44720
44721     /** @private */
44722     closeClick : function(e){
44723         var o = {};
44724         e.stopEvent();
44725         this.fireEvent("beforeclose", this, o);
44726         if(o.cancel !== true){
44727             this.tabPanel.removeTab(this.id);
44728         }
44729     },
44730     /**
44731      * The text displayed in the tooltip for the close icon.
44732      * @type String
44733      */
44734     closeText : "Close this tab"
44735 });
44736 /**
44737 *    This script refer to:
44738 *    Title: International Telephone Input
44739 *    Author: Jack O'Connor
44740 *    Code version:  v12.1.12
44741 *    Availability: https://github.com/jackocnr/intl-tel-input.git
44742 **/
44743
44744 Roo.bootstrap.form.PhoneInputData = function() {
44745     var d = [
44746       [
44747         "Afghanistan (‫افغانستان‬‎)",
44748         "af",
44749         "93"
44750       ],
44751       [
44752         "Albania (Shqipëri)",
44753         "al",
44754         "355"
44755       ],
44756       [
44757         "Algeria (‫الجزائر‬‎)",
44758         "dz",
44759         "213"
44760       ],
44761       [
44762         "American Samoa",
44763         "as",
44764         "1684"
44765       ],
44766       [
44767         "Andorra",
44768         "ad",
44769         "376"
44770       ],
44771       [
44772         "Angola",
44773         "ao",
44774         "244"
44775       ],
44776       [
44777         "Anguilla",
44778         "ai",
44779         "1264"
44780       ],
44781       [
44782         "Antigua and Barbuda",
44783         "ag",
44784         "1268"
44785       ],
44786       [
44787         "Argentina",
44788         "ar",
44789         "54"
44790       ],
44791       [
44792         "Armenia (Հայաստան)",
44793         "am",
44794         "374"
44795       ],
44796       [
44797         "Aruba",
44798         "aw",
44799         "297"
44800       ],
44801       [
44802         "Australia",
44803         "au",
44804         "61",
44805         0
44806       ],
44807       [
44808         "Austria (Österreich)",
44809         "at",
44810         "43"
44811       ],
44812       [
44813         "Azerbaijan (Azərbaycan)",
44814         "az",
44815         "994"
44816       ],
44817       [
44818         "Bahamas",
44819         "bs",
44820         "1242"
44821       ],
44822       [
44823         "Bahrain (‫البحرين‬‎)",
44824         "bh",
44825         "973"
44826       ],
44827       [
44828         "Bangladesh (বাংলাদেশ)",
44829         "bd",
44830         "880"
44831       ],
44832       [
44833         "Barbados",
44834         "bb",
44835         "1246"
44836       ],
44837       [
44838         "Belarus (Беларусь)",
44839         "by",
44840         "375"
44841       ],
44842       [
44843         "Belgium (België)",
44844         "be",
44845         "32"
44846       ],
44847       [
44848         "Belize",
44849         "bz",
44850         "501"
44851       ],
44852       [
44853         "Benin (Bénin)",
44854         "bj",
44855         "229"
44856       ],
44857       [
44858         "Bermuda",
44859         "bm",
44860         "1441"
44861       ],
44862       [
44863         "Bhutan (འབྲུག)",
44864         "bt",
44865         "975"
44866       ],
44867       [
44868         "Bolivia",
44869         "bo",
44870         "591"
44871       ],
44872       [
44873         "Bosnia and Herzegovina (Босна и Херцеговина)",
44874         "ba",
44875         "387"
44876       ],
44877       [
44878         "Botswana",
44879         "bw",
44880         "267"
44881       ],
44882       [
44883         "Brazil (Brasil)",
44884         "br",
44885         "55"
44886       ],
44887       [
44888         "British Indian Ocean Territory",
44889         "io",
44890         "246"
44891       ],
44892       [
44893         "British Virgin Islands",
44894         "vg",
44895         "1284"
44896       ],
44897       [
44898         "Brunei",
44899         "bn",
44900         "673"
44901       ],
44902       [
44903         "Bulgaria (България)",
44904         "bg",
44905         "359"
44906       ],
44907       [
44908         "Burkina Faso",
44909         "bf",
44910         "226"
44911       ],
44912       [
44913         "Burundi (Uburundi)",
44914         "bi",
44915         "257"
44916       ],
44917       [
44918         "Cambodia (កម្ពុជា)",
44919         "kh",
44920         "855"
44921       ],
44922       [
44923         "Cameroon (Cameroun)",
44924         "cm",
44925         "237"
44926       ],
44927       [
44928         "Canada",
44929         "ca",
44930         "1",
44931         1,
44932         ["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"]
44933       ],
44934       [
44935         "Cape Verde (Kabu Verdi)",
44936         "cv",
44937         "238"
44938       ],
44939       [
44940         "Caribbean Netherlands",
44941         "bq",
44942         "599",
44943         1
44944       ],
44945       [
44946         "Cayman Islands",
44947         "ky",
44948         "1345"
44949       ],
44950       [
44951         "Central African Republic (République centrafricaine)",
44952         "cf",
44953         "236"
44954       ],
44955       [
44956         "Chad (Tchad)",
44957         "td",
44958         "235"
44959       ],
44960       [
44961         "Chile",
44962         "cl",
44963         "56"
44964       ],
44965       [
44966         "China (中国)",
44967         "cn",
44968         "86"
44969       ],
44970       [
44971         "Christmas Island",
44972         "cx",
44973         "61",
44974         2
44975       ],
44976       [
44977         "Cocos (Keeling) Islands",
44978         "cc",
44979         "61",
44980         1
44981       ],
44982       [
44983         "Colombia",
44984         "co",
44985         "57"
44986       ],
44987       [
44988         "Comoros (‫جزر القمر‬‎)",
44989         "km",
44990         "269"
44991       ],
44992       [
44993         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
44994         "cd",
44995         "243"
44996       ],
44997       [
44998         "Congo (Republic) (Congo-Brazzaville)",
44999         "cg",
45000         "242"
45001       ],
45002       [
45003         "Cook Islands",
45004         "ck",
45005         "682"
45006       ],
45007       [
45008         "Costa Rica",
45009         "cr",
45010         "506"
45011       ],
45012       [
45013         "Côte d’Ivoire",
45014         "ci",
45015         "225"
45016       ],
45017       [
45018         "Croatia (Hrvatska)",
45019         "hr",
45020         "385"
45021       ],
45022       [
45023         "Cuba",
45024         "cu",
45025         "53"
45026       ],
45027       [
45028         "Curaçao",
45029         "cw",
45030         "599",
45031         0
45032       ],
45033       [
45034         "Cyprus (Κύπρος)",
45035         "cy",
45036         "357"
45037       ],
45038       [
45039         "Czech Republic (Česká republika)",
45040         "cz",
45041         "420"
45042       ],
45043       [
45044         "Denmark (Danmark)",
45045         "dk",
45046         "45"
45047       ],
45048       [
45049         "Djibouti",
45050         "dj",
45051         "253"
45052       ],
45053       [
45054         "Dominica",
45055         "dm",
45056         "1767"
45057       ],
45058       [
45059         "Dominican Republic (República Dominicana)",
45060         "do",
45061         "1",
45062         2,
45063         ["809", "829", "849"]
45064       ],
45065       [
45066         "Ecuador",
45067         "ec",
45068         "593"
45069       ],
45070       [
45071         "Egypt (‫مصر‬‎)",
45072         "eg",
45073         "20"
45074       ],
45075       [
45076         "El Salvador",
45077         "sv",
45078         "503"
45079       ],
45080       [
45081         "Equatorial Guinea (Guinea Ecuatorial)",
45082         "gq",
45083         "240"
45084       ],
45085       [
45086         "Eritrea",
45087         "er",
45088         "291"
45089       ],
45090       [
45091         "Estonia (Eesti)",
45092         "ee",
45093         "372"
45094       ],
45095       [
45096         "Ethiopia",
45097         "et",
45098         "251"
45099       ],
45100       [
45101         "Falkland Islands (Islas Malvinas)",
45102         "fk",
45103         "500"
45104       ],
45105       [
45106         "Faroe Islands (Føroyar)",
45107         "fo",
45108         "298"
45109       ],
45110       [
45111         "Fiji",
45112         "fj",
45113         "679"
45114       ],
45115       [
45116         "Finland (Suomi)",
45117         "fi",
45118         "358",
45119         0
45120       ],
45121       [
45122         "France",
45123         "fr",
45124         "33"
45125       ],
45126       [
45127         "French Guiana (Guyane française)",
45128         "gf",
45129         "594"
45130       ],
45131       [
45132         "French Polynesia (Polynésie française)",
45133         "pf",
45134         "689"
45135       ],
45136       [
45137         "Gabon",
45138         "ga",
45139         "241"
45140       ],
45141       [
45142         "Gambia",
45143         "gm",
45144         "220"
45145       ],
45146       [
45147         "Georgia (საქართველო)",
45148         "ge",
45149         "995"
45150       ],
45151       [
45152         "Germany (Deutschland)",
45153         "de",
45154         "49"
45155       ],
45156       [
45157         "Ghana (Gaana)",
45158         "gh",
45159         "233"
45160       ],
45161       [
45162         "Gibraltar",
45163         "gi",
45164         "350"
45165       ],
45166       [
45167         "Greece (Ελλάδα)",
45168         "gr",
45169         "30"
45170       ],
45171       [
45172         "Greenland (Kalaallit Nunaat)",
45173         "gl",
45174         "299"
45175       ],
45176       [
45177         "Grenada",
45178         "gd",
45179         "1473"
45180       ],
45181       [
45182         "Guadeloupe",
45183         "gp",
45184         "590",
45185         0
45186       ],
45187       [
45188         "Guam",
45189         "gu",
45190         "1671"
45191       ],
45192       [
45193         "Guatemala",
45194         "gt",
45195         "502"
45196       ],
45197       [
45198         "Guernsey",
45199         "gg",
45200         "44",
45201         1
45202       ],
45203       [
45204         "Guinea (Guinée)",
45205         "gn",
45206         "224"
45207       ],
45208       [
45209         "Guinea-Bissau (Guiné Bissau)",
45210         "gw",
45211         "245"
45212       ],
45213       [
45214         "Guyana",
45215         "gy",
45216         "592"
45217       ],
45218       [
45219         "Haiti",
45220         "ht",
45221         "509"
45222       ],
45223       [
45224         "Honduras",
45225         "hn",
45226         "504"
45227       ],
45228       [
45229         "Hong Kong (香港)",
45230         "hk",
45231         "852"
45232       ],
45233       [
45234         "Hungary (Magyarország)",
45235         "hu",
45236         "36"
45237       ],
45238       [
45239         "Iceland (Ísland)",
45240         "is",
45241         "354"
45242       ],
45243       [
45244         "India (भारत)",
45245         "in",
45246         "91"
45247       ],
45248       [
45249         "Indonesia",
45250         "id",
45251         "62"
45252       ],
45253       [
45254         "Iran (‫ایران‬‎)",
45255         "ir",
45256         "98"
45257       ],
45258       [
45259         "Iraq (‫العراق‬‎)",
45260         "iq",
45261         "964"
45262       ],
45263       [
45264         "Ireland",
45265         "ie",
45266         "353"
45267       ],
45268       [
45269         "Isle of Man",
45270         "im",
45271         "44",
45272         2
45273       ],
45274       [
45275         "Israel (‫ישראל‬‎)",
45276         "il",
45277         "972"
45278       ],
45279       [
45280         "Italy (Italia)",
45281         "it",
45282         "39",
45283         0
45284       ],
45285       [
45286         "Jamaica",
45287         "jm",
45288         "1876"
45289       ],
45290       [
45291         "Japan (日本)",
45292         "jp",
45293         "81"
45294       ],
45295       [
45296         "Jersey",
45297         "je",
45298         "44",
45299         3
45300       ],
45301       [
45302         "Jordan (‫الأردن‬‎)",
45303         "jo",
45304         "962"
45305       ],
45306       [
45307         "Kazakhstan (Казахстан)",
45308         "kz",
45309         "7",
45310         1
45311       ],
45312       [
45313         "Kenya",
45314         "ke",
45315         "254"
45316       ],
45317       [
45318         "Kiribati",
45319         "ki",
45320         "686"
45321       ],
45322       [
45323         "Kosovo",
45324         "xk",
45325         "383"
45326       ],
45327       [
45328         "Kuwait (‫الكويت‬‎)",
45329         "kw",
45330         "965"
45331       ],
45332       [
45333         "Kyrgyzstan (Кыргызстан)",
45334         "kg",
45335         "996"
45336       ],
45337       [
45338         "Laos (ລາວ)",
45339         "la",
45340         "856"
45341       ],
45342       [
45343         "Latvia (Latvija)",
45344         "lv",
45345         "371"
45346       ],
45347       [
45348         "Lebanon (‫لبنان‬‎)",
45349         "lb",
45350         "961"
45351       ],
45352       [
45353         "Lesotho",
45354         "ls",
45355         "266"
45356       ],
45357       [
45358         "Liberia",
45359         "lr",
45360         "231"
45361       ],
45362       [
45363         "Libya (‫ليبيا‬‎)",
45364         "ly",
45365         "218"
45366       ],
45367       [
45368         "Liechtenstein",
45369         "li",
45370         "423"
45371       ],
45372       [
45373         "Lithuania (Lietuva)",
45374         "lt",
45375         "370"
45376       ],
45377       [
45378         "Luxembourg",
45379         "lu",
45380         "352"
45381       ],
45382       [
45383         "Macau (澳門)",
45384         "mo",
45385         "853"
45386       ],
45387       [
45388         "Macedonia (FYROM) (Македонија)",
45389         "mk",
45390         "389"
45391       ],
45392       [
45393         "Madagascar (Madagasikara)",
45394         "mg",
45395         "261"
45396       ],
45397       [
45398         "Malawi",
45399         "mw",
45400         "265"
45401       ],
45402       [
45403         "Malaysia",
45404         "my",
45405         "60"
45406       ],
45407       [
45408         "Maldives",
45409         "mv",
45410         "960"
45411       ],
45412       [
45413         "Mali",
45414         "ml",
45415         "223"
45416       ],
45417       [
45418         "Malta",
45419         "mt",
45420         "356"
45421       ],
45422       [
45423         "Marshall Islands",
45424         "mh",
45425         "692"
45426       ],
45427       [
45428         "Martinique",
45429         "mq",
45430         "596"
45431       ],
45432       [
45433         "Mauritania (‫موريتانيا‬‎)",
45434         "mr",
45435         "222"
45436       ],
45437       [
45438         "Mauritius (Moris)",
45439         "mu",
45440         "230"
45441       ],
45442       [
45443         "Mayotte",
45444         "yt",
45445         "262",
45446         1
45447       ],
45448       [
45449         "Mexico (México)",
45450         "mx",
45451         "52"
45452       ],
45453       [
45454         "Micronesia",
45455         "fm",
45456         "691"
45457       ],
45458       [
45459         "Moldova (Republica Moldova)",
45460         "md",
45461         "373"
45462       ],
45463       [
45464         "Monaco",
45465         "mc",
45466         "377"
45467       ],
45468       [
45469         "Mongolia (Монгол)",
45470         "mn",
45471         "976"
45472       ],
45473       [
45474         "Montenegro (Crna Gora)",
45475         "me",
45476         "382"
45477       ],
45478       [
45479         "Montserrat",
45480         "ms",
45481         "1664"
45482       ],
45483       [
45484         "Morocco (‫المغرب‬‎)",
45485         "ma",
45486         "212",
45487         0
45488       ],
45489       [
45490         "Mozambique (Moçambique)",
45491         "mz",
45492         "258"
45493       ],
45494       [
45495         "Myanmar (Burma) (မြန်မာ)",
45496         "mm",
45497         "95"
45498       ],
45499       [
45500         "Namibia (Namibië)",
45501         "na",
45502         "264"
45503       ],
45504       [
45505         "Nauru",
45506         "nr",
45507         "674"
45508       ],
45509       [
45510         "Nepal (नेपाल)",
45511         "np",
45512         "977"
45513       ],
45514       [
45515         "Netherlands (Nederland)",
45516         "nl",
45517         "31"
45518       ],
45519       [
45520         "New Caledonia (Nouvelle-Calédonie)",
45521         "nc",
45522         "687"
45523       ],
45524       [
45525         "New Zealand",
45526         "nz",
45527         "64"
45528       ],
45529       [
45530         "Nicaragua",
45531         "ni",
45532         "505"
45533       ],
45534       [
45535         "Niger (Nijar)",
45536         "ne",
45537         "227"
45538       ],
45539       [
45540         "Nigeria",
45541         "ng",
45542         "234"
45543       ],
45544       [
45545         "Niue",
45546         "nu",
45547         "683"
45548       ],
45549       [
45550         "Norfolk Island",
45551         "nf",
45552         "672"
45553       ],
45554       [
45555         "North Korea (조선 민주주의 인민 공화국)",
45556         "kp",
45557         "850"
45558       ],
45559       [
45560         "Northern Mariana Islands",
45561         "mp",
45562         "1670"
45563       ],
45564       [
45565         "Norway (Norge)",
45566         "no",
45567         "47",
45568         0
45569       ],
45570       [
45571         "Oman (‫عُمان‬‎)",
45572         "om",
45573         "968"
45574       ],
45575       [
45576         "Pakistan (‫پاکستان‬‎)",
45577         "pk",
45578         "92"
45579       ],
45580       [
45581         "Palau",
45582         "pw",
45583         "680"
45584       ],
45585       [
45586         "Palestine (‫فلسطين‬‎)",
45587         "ps",
45588         "970"
45589       ],
45590       [
45591         "Panama (Panamá)",
45592         "pa",
45593         "507"
45594       ],
45595       [
45596         "Papua New Guinea",
45597         "pg",
45598         "675"
45599       ],
45600       [
45601         "Paraguay",
45602         "py",
45603         "595"
45604       ],
45605       [
45606         "Peru (Perú)",
45607         "pe",
45608         "51"
45609       ],
45610       [
45611         "Philippines",
45612         "ph",
45613         "63"
45614       ],
45615       [
45616         "Poland (Polska)",
45617         "pl",
45618         "48"
45619       ],
45620       [
45621         "Portugal",
45622         "pt",
45623         "351"
45624       ],
45625       [
45626         "Puerto Rico",
45627         "pr",
45628         "1",
45629         3,
45630         ["787", "939"]
45631       ],
45632       [
45633         "Qatar (‫قطر‬‎)",
45634         "qa",
45635         "974"
45636       ],
45637       [
45638         "Réunion (La Réunion)",
45639         "re",
45640         "262",
45641         0
45642       ],
45643       [
45644         "Romania (România)",
45645         "ro",
45646         "40"
45647       ],
45648       [
45649         "Russia (Россия)",
45650         "ru",
45651         "7",
45652         0
45653       ],
45654       [
45655         "Rwanda",
45656         "rw",
45657         "250"
45658       ],
45659       [
45660         "Saint Barthélemy",
45661         "bl",
45662         "590",
45663         1
45664       ],
45665       [
45666         "Saint Helena",
45667         "sh",
45668         "290"
45669       ],
45670       [
45671         "Saint Kitts and Nevis",
45672         "kn",
45673         "1869"
45674       ],
45675       [
45676         "Saint Lucia",
45677         "lc",
45678         "1758"
45679       ],
45680       [
45681         "Saint Martin (Saint-Martin (partie française))",
45682         "mf",
45683         "590",
45684         2
45685       ],
45686       [
45687         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
45688         "pm",
45689         "508"
45690       ],
45691       [
45692         "Saint Vincent and the Grenadines",
45693         "vc",
45694         "1784"
45695       ],
45696       [
45697         "Samoa",
45698         "ws",
45699         "685"
45700       ],
45701       [
45702         "San Marino",
45703         "sm",
45704         "378"
45705       ],
45706       [
45707         "São Tomé and Príncipe (São Tomé e Príncipe)",
45708         "st",
45709         "239"
45710       ],
45711       [
45712         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
45713         "sa",
45714         "966"
45715       ],
45716       [
45717         "Senegal (Sénégal)",
45718         "sn",
45719         "221"
45720       ],
45721       [
45722         "Serbia (Србија)",
45723         "rs",
45724         "381"
45725       ],
45726       [
45727         "Seychelles",
45728         "sc",
45729         "248"
45730       ],
45731       [
45732         "Sierra Leone",
45733         "sl",
45734         "232"
45735       ],
45736       [
45737         "Singapore",
45738         "sg",
45739         "65"
45740       ],
45741       [
45742         "Sint Maarten",
45743         "sx",
45744         "1721"
45745       ],
45746       [
45747         "Slovakia (Slovensko)",
45748         "sk",
45749         "421"
45750       ],
45751       [
45752         "Slovenia (Slovenija)",
45753         "si",
45754         "386"
45755       ],
45756       [
45757         "Solomon Islands",
45758         "sb",
45759         "677"
45760       ],
45761       [
45762         "Somalia (Soomaaliya)",
45763         "so",
45764         "252"
45765       ],
45766       [
45767         "South Africa",
45768         "za",
45769         "27"
45770       ],
45771       [
45772         "South Korea (대한민국)",
45773         "kr",
45774         "82"
45775       ],
45776       [
45777         "South Sudan (‫جنوب السودان‬‎)",
45778         "ss",
45779         "211"
45780       ],
45781       [
45782         "Spain (España)",
45783         "es",
45784         "34"
45785       ],
45786       [
45787         "Sri Lanka (ශ්‍රී ලංකාව)",
45788         "lk",
45789         "94"
45790       ],
45791       [
45792         "Sudan (‫السودان‬‎)",
45793         "sd",
45794         "249"
45795       ],
45796       [
45797         "Suriname",
45798         "sr",
45799         "597"
45800       ],
45801       [
45802         "Svalbard and Jan Mayen",
45803         "sj",
45804         "47",
45805         1
45806       ],
45807       [
45808         "Swaziland",
45809         "sz",
45810         "268"
45811       ],
45812       [
45813         "Sweden (Sverige)",
45814         "se",
45815         "46"
45816       ],
45817       [
45818         "Switzerland (Schweiz)",
45819         "ch",
45820         "41"
45821       ],
45822       [
45823         "Syria (‫سوريا‬‎)",
45824         "sy",
45825         "963"
45826       ],
45827       [
45828         "Taiwan (台灣)",
45829         "tw",
45830         "886"
45831       ],
45832       [
45833         "Tajikistan",
45834         "tj",
45835         "992"
45836       ],
45837       [
45838         "Tanzania",
45839         "tz",
45840         "255"
45841       ],
45842       [
45843         "Thailand (ไทย)",
45844         "th",
45845         "66"
45846       ],
45847       [
45848         "Timor-Leste",
45849         "tl",
45850         "670"
45851       ],
45852       [
45853         "Togo",
45854         "tg",
45855         "228"
45856       ],
45857       [
45858         "Tokelau",
45859         "tk",
45860         "690"
45861       ],
45862       [
45863         "Tonga",
45864         "to",
45865         "676"
45866       ],
45867       [
45868         "Trinidad and Tobago",
45869         "tt",
45870         "1868"
45871       ],
45872       [
45873         "Tunisia (‫تونس‬‎)",
45874         "tn",
45875         "216"
45876       ],
45877       [
45878         "Turkey (Türkiye)",
45879         "tr",
45880         "90"
45881       ],
45882       [
45883         "Turkmenistan",
45884         "tm",
45885         "993"
45886       ],
45887       [
45888         "Turks and Caicos Islands",
45889         "tc",
45890         "1649"
45891       ],
45892       [
45893         "Tuvalu",
45894         "tv",
45895         "688"
45896       ],
45897       [
45898         "U.S. Virgin Islands",
45899         "vi",
45900         "1340"
45901       ],
45902       [
45903         "Uganda",
45904         "ug",
45905         "256"
45906       ],
45907       [
45908         "Ukraine (Україна)",
45909         "ua",
45910         "380"
45911       ],
45912       [
45913         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
45914         "ae",
45915         "971"
45916       ],
45917       [
45918         "United Kingdom",
45919         "gb",
45920         "44",
45921         0
45922       ],
45923       [
45924         "United States",
45925         "us",
45926         "1",
45927         0
45928       ],
45929       [
45930         "Uruguay",
45931         "uy",
45932         "598"
45933       ],
45934       [
45935         "Uzbekistan (Oʻzbekiston)",
45936         "uz",
45937         "998"
45938       ],
45939       [
45940         "Vanuatu",
45941         "vu",
45942         "678"
45943       ],
45944       [
45945         "Vatican City (Città del Vaticano)",
45946         "va",
45947         "39",
45948         1
45949       ],
45950       [
45951         "Venezuela",
45952         "ve",
45953         "58"
45954       ],
45955       [
45956         "Vietnam (Việt Nam)",
45957         "vn",
45958         "84"
45959       ],
45960       [
45961         "Wallis and Futuna (Wallis-et-Futuna)",
45962         "wf",
45963         "681"
45964       ],
45965       [
45966         "Western Sahara (‫الصحراء الغربية‬‎)",
45967         "eh",
45968         "212",
45969         1
45970       ],
45971       [
45972         "Yemen (‫اليمن‬‎)",
45973         "ye",
45974         "967"
45975       ],
45976       [
45977         "Zambia",
45978         "zm",
45979         "260"
45980       ],
45981       [
45982         "Zimbabwe",
45983         "zw",
45984         "263"
45985       ],
45986       [
45987         "Åland Islands",
45988         "ax",
45989         "358",
45990         1
45991       ]
45992   ];
45993   
45994   return d;
45995 }/**
45996 *    This script refer to:
45997 *    Title: International Telephone Input
45998 *    Author: Jack O'Connor
45999 *    Code version:  v12.1.12
46000 *    Availability: https://github.com/jackocnr/intl-tel-input.git
46001 **/
46002
46003 /**
46004  * @class Roo.bootstrap.form.PhoneInput
46005  * @extends Roo.bootstrap.form.TriggerField
46006  * An input with International dial-code selection
46007  
46008  * @cfg {String} defaultDialCode default '+852'
46009  * @cfg {Array} preferedCountries default []
46010   
46011  * @constructor
46012  * Create a new PhoneInput.
46013  * @param {Object} config Configuration options
46014  */
46015
46016 Roo.bootstrap.form.PhoneInput = function(config) {
46017     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
46018 };
46019
46020 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
46021         /**
46022         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
46023         */
46024         listWidth: undefined,
46025         
46026         selectedClass: 'active',
46027         
46028         invalidClass : "has-warning",
46029         
46030         validClass: 'has-success',
46031         
46032         allowed: '0123456789',
46033         
46034         max_length: 15,
46035         
46036         /**
46037          * @cfg {String} defaultDialCode The default dial code when initializing the input
46038          */
46039         defaultDialCode: '+852',
46040         
46041         /**
46042          * @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
46043          */
46044         preferedCountries: false,
46045         
46046         getAutoCreate : function()
46047         {
46048             var data = Roo.bootstrap.form.PhoneInputData();
46049             var align = this.labelAlign || this.parentLabelAlign();
46050             var id = Roo.id();
46051             
46052             this.allCountries = [];
46053             this.dialCodeMapping = [];
46054             
46055             for (var i = 0; i < data.length; i++) {
46056               var c = data[i];
46057               this.allCountries[i] = {
46058                 name: c[0],
46059                 iso2: c[1],
46060                 dialCode: c[2],
46061                 priority: c[3] || 0,
46062                 areaCodes: c[4] || null
46063               };
46064               this.dialCodeMapping[c[2]] = {
46065                   name: c[0],
46066                   iso2: c[1],
46067                   priority: c[3] || 0,
46068                   areaCodes: c[4] || null
46069               };
46070             }
46071             
46072             var cfg = {
46073                 cls: 'form-group',
46074                 cn: []
46075             };
46076             
46077             var input =  {
46078                 tag: 'input',
46079                 id : id,
46080                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
46081                 maxlength: this.max_length,
46082                 cls : 'form-control tel-input',
46083                 autocomplete: 'new-password'
46084             };
46085             
46086             var hiddenInput = {
46087                 tag: 'input',
46088                 type: 'hidden',
46089                 cls: 'hidden-tel-input'
46090             };
46091             
46092             if (this.name) {
46093                 hiddenInput.name = this.name;
46094             }
46095             
46096             if (this.disabled) {
46097                 input.disabled = true;
46098             }
46099             
46100             var flag_container = {
46101                 tag: 'div',
46102                 cls: 'flag-box',
46103                 cn: [
46104                     {
46105                         tag: 'div',
46106                         cls: 'flag'
46107                     },
46108                     {
46109                         tag: 'div',
46110                         cls: 'caret'
46111                     }
46112                 ]
46113             };
46114             
46115             var box = {
46116                 tag: 'div',
46117                 cls: this.hasFeedback ? 'has-feedback' : '',
46118                 cn: [
46119                     hiddenInput,
46120                     input,
46121                     {
46122                         tag: 'input',
46123                         cls: 'dial-code-holder',
46124                         disabled: true
46125                     }
46126                 ]
46127             };
46128             
46129             var container = {
46130                 cls: 'roo-select2-container input-group',
46131                 cn: [
46132                     flag_container,
46133                     box
46134                 ]
46135             };
46136             
46137             if (this.fieldLabel.length) {
46138                 var indicator = {
46139                     tag: 'i',
46140                     tooltip: 'This field is required'
46141                 };
46142                 
46143                 var label = {
46144                     tag: 'label',
46145                     'for':  id,
46146                     cls: 'control-label',
46147                     cn: []
46148                 };
46149                 
46150                 var label_text = {
46151                     tag: 'span',
46152                     html: this.fieldLabel
46153                 };
46154                 
46155                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46156                 label.cn = [
46157                     indicator,
46158                     label_text
46159                 ];
46160                 
46161                 if(this.indicatorpos == 'right') {
46162                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46163                     label.cn = [
46164                         label_text,
46165                         indicator
46166                     ];
46167                 }
46168                 
46169                 if(align == 'left') {
46170                     container = {
46171                         tag: 'div',
46172                         cn: [
46173                             container
46174                         ]
46175                     };
46176                     
46177                     if(this.labelWidth > 12){
46178                         label.style = "width: " + this.labelWidth + 'px';
46179                     }
46180                     if(this.labelWidth < 13 && this.labelmd == 0){
46181                         this.labelmd = this.labelWidth;
46182                     }
46183                     if(this.labellg > 0){
46184                         label.cls += ' col-lg-' + this.labellg;
46185                         input.cls += ' col-lg-' + (12 - this.labellg);
46186                     }
46187                     if(this.labelmd > 0){
46188                         label.cls += ' col-md-' + this.labelmd;
46189                         container.cls += ' col-md-' + (12 - this.labelmd);
46190                     }
46191                     if(this.labelsm > 0){
46192                         label.cls += ' col-sm-' + this.labelsm;
46193                         container.cls += ' col-sm-' + (12 - this.labelsm);
46194                     }
46195                     if(this.labelxs > 0){
46196                         label.cls += ' col-xs-' + this.labelxs;
46197                         container.cls += ' col-xs-' + (12 - this.labelxs);
46198                     }
46199                 }
46200             }
46201             
46202             cfg.cn = [
46203                 label,
46204                 container
46205             ];
46206             
46207             var settings = this;
46208             
46209             ['xs','sm','md','lg'].map(function(size){
46210                 if (settings[size]) {
46211                     cfg.cls += ' col-' + size + '-' + settings[size];
46212                 }
46213             });
46214             
46215             this.store = new Roo.data.Store({
46216                 proxy : new Roo.data.MemoryProxy({}),
46217                 reader : new Roo.data.JsonReader({
46218                     fields : [
46219                         {
46220                             'name' : 'name',
46221                             'type' : 'string'
46222                         },
46223                         {
46224                             'name' : 'iso2',
46225                             'type' : 'string'
46226                         },
46227                         {
46228                             'name' : 'dialCode',
46229                             'type' : 'string'
46230                         },
46231                         {
46232                             'name' : 'priority',
46233                             'type' : 'string'
46234                         },
46235                         {
46236                             'name' : 'areaCodes',
46237                             'type' : 'string'
46238                         }
46239                     ]
46240                 })
46241             });
46242             
46243             if(!this.preferedCountries) {
46244                 this.preferedCountries = [
46245                     'hk',
46246                     'gb',
46247                     'us'
46248                 ];
46249             }
46250             
46251             var p = this.preferedCountries.reverse();
46252             
46253             if(p) {
46254                 for (var i = 0; i < p.length; i++) {
46255                     for (var j = 0; j < this.allCountries.length; j++) {
46256                         if(this.allCountries[j].iso2 == p[i]) {
46257                             var t = this.allCountries[j];
46258                             this.allCountries.splice(j,1);
46259                             this.allCountries.unshift(t);
46260                         }
46261                     } 
46262                 }
46263             }
46264             
46265             this.store.proxy.data = {
46266                 success: true,
46267                 data: this.allCountries
46268             };
46269             
46270             return cfg;
46271         },
46272         
46273         initEvents : function()
46274         {
46275             this.createList();
46276             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
46277             
46278             this.indicator = this.indicatorEl();
46279             this.flag = this.flagEl();
46280             this.dialCodeHolder = this.dialCodeHolderEl();
46281             
46282             this.trigger = this.el.select('div.flag-box',true).first();
46283             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
46284             
46285             var _this = this;
46286             
46287             (function(){
46288                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
46289                 _this.list.setWidth(lw);
46290             }).defer(100);
46291             
46292             this.list.on('mouseover', this.onViewOver, this);
46293             this.list.on('mousemove', this.onViewMove, this);
46294             this.inputEl().on("keyup", this.onKeyUp, this);
46295             this.inputEl().on("keypress", this.onKeyPress, this);
46296             
46297             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
46298
46299             this.view = new Roo.View(this.list, this.tpl, {
46300                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
46301             });
46302             
46303             this.view.on('click', this.onViewClick, this);
46304             this.setValue(this.defaultDialCode);
46305         },
46306         
46307         onTriggerClick : function(e)
46308         {
46309             Roo.log('trigger click');
46310             if(this.disabled){
46311                 return;
46312             }
46313             
46314             if(this.isExpanded()){
46315                 this.collapse();
46316                 this.hasFocus = false;
46317             }else {
46318                 this.store.load({});
46319                 this.hasFocus = true;
46320                 this.expand();
46321             }
46322         },
46323         
46324         isExpanded : function()
46325         {
46326             return this.list.isVisible();
46327         },
46328         
46329         collapse : function()
46330         {
46331             if(!this.isExpanded()){
46332                 return;
46333             }
46334             this.list.hide();
46335             Roo.get(document).un('mousedown', this.collapseIf, this);
46336             Roo.get(document).un('mousewheel', this.collapseIf, this);
46337             this.fireEvent('collapse', this);
46338             this.validate();
46339         },
46340         
46341         expand : function()
46342         {
46343             Roo.log('expand');
46344
46345             if(this.isExpanded() || !this.hasFocus){
46346                 return;
46347             }
46348             
46349             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
46350             this.list.setWidth(lw);
46351             
46352             this.list.show();
46353             this.restrictHeight();
46354             
46355             Roo.get(document).on('mousedown', this.collapseIf, this);
46356             Roo.get(document).on('mousewheel', this.collapseIf, this);
46357             
46358             this.fireEvent('expand', this);
46359         },
46360         
46361         restrictHeight : function()
46362         {
46363             this.list.alignTo(this.inputEl(), this.listAlign);
46364             this.list.alignTo(this.inputEl(), this.listAlign);
46365         },
46366         
46367         onViewOver : function(e, t)
46368         {
46369             if(this.inKeyMode){
46370                 return;
46371             }
46372             var item = this.view.findItemFromChild(t);
46373             
46374             if(item){
46375                 var index = this.view.indexOf(item);
46376                 this.select(index, false);
46377             }
46378         },
46379
46380         // private
46381         onViewClick : function(view, doFocus, el, e)
46382         {
46383             var index = this.view.getSelectedIndexes()[0];
46384             
46385             var r = this.store.getAt(index);
46386             
46387             if(r){
46388                 this.onSelect(r, index);
46389             }
46390             if(doFocus !== false && !this.blockFocus){
46391                 this.inputEl().focus();
46392             }
46393         },
46394         
46395         onViewMove : function(e, t)
46396         {
46397             this.inKeyMode = false;
46398         },
46399         
46400         select : function(index, scrollIntoView)
46401         {
46402             this.selectedIndex = index;
46403             this.view.select(index);
46404             if(scrollIntoView !== false){
46405                 var el = this.view.getNode(index);
46406                 if(el){
46407                     this.list.scrollChildIntoView(el, false);
46408                 }
46409             }
46410         },
46411         
46412         createList : function()
46413         {
46414             this.list = Roo.get(document.body).createChild({
46415                 tag: 'ul',
46416                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
46417                 style: 'display:none'
46418             });
46419             
46420             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
46421         },
46422         
46423         collapseIf : function(e)
46424         {
46425             var in_combo  = e.within(this.el);
46426             var in_list =  e.within(this.list);
46427             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
46428             
46429             if (in_combo || in_list || is_list) {
46430                 return;
46431             }
46432             this.collapse();
46433         },
46434         
46435         onSelect : function(record, index)
46436         {
46437             if(this.fireEvent('beforeselect', this, record, index) !== false){
46438                 
46439                 this.setFlagClass(record.data.iso2);
46440                 this.setDialCode(record.data.dialCode);
46441                 this.hasFocus = false;
46442                 this.collapse();
46443                 this.fireEvent('select', this, record, index);
46444             }
46445         },
46446         
46447         flagEl : function()
46448         {
46449             var flag = this.el.select('div.flag',true).first();
46450             if(!flag){
46451                 return false;
46452             }
46453             return flag;
46454         },
46455         
46456         dialCodeHolderEl : function()
46457         {
46458             var d = this.el.select('input.dial-code-holder',true).first();
46459             if(!d){
46460                 return false;
46461             }
46462             return d;
46463         },
46464         
46465         setDialCode : function(v)
46466         {
46467             this.dialCodeHolder.dom.value = '+'+v;
46468         },
46469         
46470         setFlagClass : function(n)
46471         {
46472             this.flag.dom.className = 'flag '+n;
46473         },
46474         
46475         getValue : function()
46476         {
46477             var v = this.inputEl().getValue();
46478             if(this.dialCodeHolder) {
46479                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
46480             }
46481             return v;
46482         },
46483         
46484         setValue : function(v)
46485         {
46486             var d = this.getDialCode(v);
46487             
46488             //invalid dial code
46489             if(v.length == 0 || !d || d.length == 0) {
46490                 if(this.rendered){
46491                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
46492                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
46493                 }
46494                 return;
46495             }
46496             
46497             //valid dial code
46498             this.setFlagClass(this.dialCodeMapping[d].iso2);
46499             this.setDialCode(d);
46500             this.inputEl().dom.value = v.replace('+'+d,'');
46501             this.hiddenEl().dom.value = this.getValue();
46502             
46503             this.validate();
46504         },
46505         
46506         getDialCode : function(v)
46507         {
46508             v = v ||  '';
46509             
46510             if (v.length == 0) {
46511                 return this.dialCodeHolder.dom.value;
46512             }
46513             
46514             var dialCode = "";
46515             if (v.charAt(0) != "+") {
46516                 return false;
46517             }
46518             var numericChars = "";
46519             for (var i = 1; i < v.length; i++) {
46520               var c = v.charAt(i);
46521               if (!isNaN(c)) {
46522                 numericChars += c;
46523                 if (this.dialCodeMapping[numericChars]) {
46524                   dialCode = v.substr(1, i);
46525                 }
46526                 if (numericChars.length == 4) {
46527                   break;
46528                 }
46529               }
46530             }
46531             return dialCode;
46532         },
46533         
46534         reset : function()
46535         {
46536             this.setValue(this.defaultDialCode);
46537             this.validate();
46538         },
46539         
46540         hiddenEl : function()
46541         {
46542             return this.el.select('input.hidden-tel-input',true).first();
46543         },
46544         
46545         // after setting val
46546         onKeyUp : function(e){
46547             this.setValue(this.getValue());
46548         },
46549         
46550         onKeyPress : function(e){
46551             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
46552                 e.stopEvent();
46553             }
46554         }
46555         
46556 });
46557 /**
46558  * @class Roo.bootstrap.form.MoneyField
46559  * @extends Roo.bootstrap.form.ComboBox
46560  * Bootstrap MoneyField class
46561  * 
46562  * @constructor
46563  * Create a new MoneyField.
46564  * @param {Object} config Configuration options
46565  */
46566
46567 Roo.bootstrap.form.MoneyField = function(config) {
46568     
46569     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
46570     
46571 };
46572
46573 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
46574     
46575     /**
46576      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
46577      */
46578     allowDecimals : true,
46579     /**
46580      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
46581      */
46582     decimalSeparator : ".",
46583     /**
46584      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
46585      */
46586     decimalPrecision : 0,
46587     /**
46588      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
46589      */
46590     allowNegative : true,
46591     /**
46592      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
46593      */
46594     allowZero: true,
46595     /**
46596      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
46597      */
46598     minValue : Number.NEGATIVE_INFINITY,
46599     /**
46600      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
46601      */
46602     maxValue : Number.MAX_VALUE,
46603     /**
46604      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
46605      */
46606     minText : "The minimum value for this field is {0}",
46607     /**
46608      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
46609      */
46610     maxText : "The maximum value for this field is {0}",
46611     /**
46612      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
46613      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
46614      */
46615     nanText : "{0} is not a valid number",
46616     /**
46617      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
46618      */
46619     castInt : true,
46620     /**
46621      * @cfg {String} defaults currency of the MoneyField
46622      * value should be in lkey
46623      */
46624     defaultCurrency : false,
46625     /**
46626      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
46627      */
46628     thousandsDelimiter : false,
46629     /**
46630      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
46631      */
46632     max_length: false,
46633     
46634     inputlg : 9,
46635     inputmd : 9,
46636     inputsm : 9,
46637     inputxs : 6,
46638      /**
46639      * @cfg {Roo.data.Store} store  Store to lookup currency??
46640      */
46641     store : false,
46642     
46643     getAutoCreate : function()
46644     {
46645         var align = this.labelAlign || this.parentLabelAlign();
46646         
46647         var id = Roo.id();
46648
46649         var cfg = {
46650             cls: 'form-group',
46651             cn: []
46652         };
46653
46654         var input =  {
46655             tag: 'input',
46656             id : id,
46657             cls : 'form-control roo-money-amount-input',
46658             autocomplete: 'new-password'
46659         };
46660         
46661         var hiddenInput = {
46662             tag: 'input',
46663             type: 'hidden',
46664             id: Roo.id(),
46665             cls: 'hidden-number-input'
46666         };
46667         
46668         if(this.max_length) {
46669             input.maxlength = this.max_length; 
46670         }
46671         
46672         if (this.name) {
46673             hiddenInput.name = this.name;
46674         }
46675
46676         if (this.disabled) {
46677             input.disabled = true;
46678         }
46679
46680         var clg = 12 - this.inputlg;
46681         var cmd = 12 - this.inputmd;
46682         var csm = 12 - this.inputsm;
46683         var cxs = 12 - this.inputxs;
46684         
46685         var container = {
46686             tag : 'div',
46687             cls : 'row roo-money-field',
46688             cn : [
46689                 {
46690                     tag : 'div',
46691                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
46692                     cn : [
46693                         {
46694                             tag : 'div',
46695                             cls: 'roo-select2-container input-group',
46696                             cn: [
46697                                 {
46698                                     tag : 'input',
46699                                     cls : 'form-control roo-money-currency-input',
46700                                     autocomplete: 'new-password',
46701                                     readOnly : 1,
46702                                     name : this.currencyName
46703                                 },
46704                                 {
46705                                     tag :'span',
46706                                     cls : 'input-group-addon',
46707                                     cn : [
46708                                         {
46709                                             tag: 'span',
46710                                             cls: 'caret'
46711                                         }
46712                                     ]
46713                                 }
46714                             ]
46715                         }
46716                     ]
46717                 },
46718                 {
46719                     tag : 'div',
46720                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
46721                     cn : [
46722                         {
46723                             tag: 'div',
46724                             cls: this.hasFeedback ? 'has-feedback' : '',
46725                             cn: [
46726                                 input
46727                             ]
46728                         }
46729                     ]
46730                 }
46731             ]
46732             
46733         };
46734         
46735         if (this.fieldLabel.length) {
46736             var indicator = {
46737                 tag: 'i',
46738                 tooltip: 'This field is required'
46739             };
46740
46741             var label = {
46742                 tag: 'label',
46743                 'for':  id,
46744                 cls: 'control-label',
46745                 cn: []
46746             };
46747
46748             var label_text = {
46749                 tag: 'span',
46750                 html: this.fieldLabel
46751             };
46752
46753             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46754             label.cn = [
46755                 indicator,
46756                 label_text
46757             ];
46758
46759             if(this.indicatorpos == 'right') {
46760                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46761                 label.cn = [
46762                     label_text,
46763                     indicator
46764                 ];
46765             }
46766
46767             if(align == 'left') {
46768                 container = {
46769                     tag: 'div',
46770                     cn: [
46771                         container
46772                     ]
46773                 };
46774
46775                 if(this.labelWidth > 12){
46776                     label.style = "width: " + this.labelWidth + 'px';
46777                 }
46778                 if(this.labelWidth < 13 && this.labelmd == 0){
46779                     this.labelmd = this.labelWidth;
46780                 }
46781                 if(this.labellg > 0){
46782                     label.cls += ' col-lg-' + this.labellg;
46783                     input.cls += ' col-lg-' + (12 - this.labellg);
46784                 }
46785                 if(this.labelmd > 0){
46786                     label.cls += ' col-md-' + this.labelmd;
46787                     container.cls += ' col-md-' + (12 - this.labelmd);
46788                 }
46789                 if(this.labelsm > 0){
46790                     label.cls += ' col-sm-' + this.labelsm;
46791                     container.cls += ' col-sm-' + (12 - this.labelsm);
46792                 }
46793                 if(this.labelxs > 0){
46794                     label.cls += ' col-xs-' + this.labelxs;
46795                     container.cls += ' col-xs-' + (12 - this.labelxs);
46796                 }
46797             }
46798         }
46799
46800         cfg.cn = [
46801             label,
46802             container,
46803             hiddenInput
46804         ];
46805         
46806         var settings = this;
46807
46808         ['xs','sm','md','lg'].map(function(size){
46809             if (settings[size]) {
46810                 cfg.cls += ' col-' + size + '-' + settings[size];
46811             }
46812         });
46813         
46814         return cfg;
46815     },
46816     
46817     initEvents : function()
46818     {
46819         this.indicator = this.indicatorEl();
46820         
46821         this.initCurrencyEvent();
46822         
46823         this.initNumberEvent();
46824     },
46825     
46826     initCurrencyEvent : function()
46827     {
46828         if (!this.store) {
46829             throw "can not find store for combo";
46830         }
46831         
46832         this.store = Roo.factory(this.store, Roo.data);
46833         this.store.parent = this;
46834         
46835         this.createList();
46836         
46837         this.triggerEl = this.el.select('.input-group-addon', true).first();
46838         
46839         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
46840         
46841         var _this = this;
46842         
46843         (function(){
46844             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
46845             _this.list.setWidth(lw);
46846         }).defer(100);
46847         
46848         this.list.on('mouseover', this.onViewOver, this);
46849         this.list.on('mousemove', this.onViewMove, this);
46850         this.list.on('scroll', this.onViewScroll, this);
46851         
46852         if(!this.tpl){
46853             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
46854         }
46855         
46856         this.view = new Roo.View(this.list, this.tpl, {
46857             singleSelect:true, store: this.store, selectedClass: this.selectedClass
46858         });
46859         
46860         this.view.on('click', this.onViewClick, this);
46861         
46862         this.store.on('beforeload', this.onBeforeLoad, this);
46863         this.store.on('load', this.onLoad, this);
46864         this.store.on('loadexception', this.onLoadException, this);
46865         
46866         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
46867             "up" : function(e){
46868                 this.inKeyMode = true;
46869                 this.selectPrev();
46870             },
46871
46872             "down" : function(e){
46873                 if(!this.isExpanded()){
46874                     this.onTriggerClick();
46875                 }else{
46876                     this.inKeyMode = true;
46877                     this.selectNext();
46878                 }
46879             },
46880
46881             "enter" : function(e){
46882                 this.collapse();
46883                 
46884                 if(this.fireEvent("specialkey", this, e)){
46885                     this.onViewClick(false);
46886                 }
46887                 
46888                 return true;
46889             },
46890
46891             "esc" : function(e){
46892                 this.collapse();
46893             },
46894
46895             "tab" : function(e){
46896                 this.collapse();
46897                 
46898                 if(this.fireEvent("specialkey", this, e)){
46899                     this.onViewClick(false);
46900                 }
46901                 
46902                 return true;
46903             },
46904
46905             scope : this,
46906
46907             doRelay : function(foo, bar, hname){
46908                 if(hname == 'down' || this.scope.isExpanded()){
46909                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
46910                 }
46911                 return true;
46912             },
46913
46914             forceKeyDown: true
46915         });
46916         
46917         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
46918         
46919     },
46920     
46921     initNumberEvent : function(e)
46922     {
46923         this.inputEl().on("keydown" , this.fireKey,  this);
46924         this.inputEl().on("focus", this.onFocus,  this);
46925         this.inputEl().on("blur", this.onBlur,  this);
46926         
46927         this.inputEl().relayEvent('keyup', this);
46928         
46929         if(this.indicator){
46930             this.indicator.addClass('invisible');
46931         }
46932  
46933         this.originalValue = this.getValue();
46934         
46935         if(this.validationEvent == 'keyup'){
46936             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
46937             this.inputEl().on('keyup', this.filterValidation, this);
46938         }
46939         else if(this.validationEvent !== false){
46940             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
46941         }
46942         
46943         if(this.selectOnFocus){
46944             this.on("focus", this.preFocus, this);
46945             
46946         }
46947         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
46948             this.inputEl().on("keypress", this.filterKeys, this);
46949         } else {
46950             this.inputEl().relayEvent('keypress', this);
46951         }
46952         
46953         var allowed = "0123456789";
46954         
46955         if(this.allowDecimals){
46956             allowed += this.decimalSeparator;
46957         }
46958         
46959         if(this.allowNegative){
46960             allowed += "-";
46961         }
46962         
46963         if(this.thousandsDelimiter) {
46964             allowed += ",";
46965         }
46966         
46967         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
46968         
46969         var keyPress = function(e){
46970             
46971             var k = e.getKey();
46972             
46973             var c = e.getCharCode();
46974             
46975             if(
46976                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
46977                     allowed.indexOf(String.fromCharCode(c)) === -1
46978             ){
46979                 e.stopEvent();
46980                 return;
46981             }
46982             
46983             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
46984                 return;
46985             }
46986             
46987             if(allowed.indexOf(String.fromCharCode(c)) === -1){
46988                 e.stopEvent();
46989             }
46990         };
46991         
46992         this.inputEl().on("keypress", keyPress, this);
46993         
46994     },
46995     
46996     onTriggerClick : function(e)
46997     {   
46998         if(this.disabled){
46999             return;
47000         }
47001         
47002         this.page = 0;
47003         this.loadNext = false;
47004         
47005         if(this.isExpanded()){
47006             this.collapse();
47007             return;
47008         }
47009         
47010         this.hasFocus = true;
47011         
47012         if(this.triggerAction == 'all') {
47013             this.doQuery(this.allQuery, true);
47014             return;
47015         }
47016         
47017         this.doQuery(this.getRawValue());
47018     },
47019     
47020     getCurrency : function()
47021     {   
47022         var v = this.currencyEl().getValue();
47023         
47024         return v;
47025     },
47026     
47027     restrictHeight : function()
47028     {
47029         this.list.alignTo(this.currencyEl(), this.listAlign);
47030         this.list.alignTo(this.currencyEl(), this.listAlign);
47031     },
47032     
47033     onViewClick : function(view, doFocus, el, e)
47034     {
47035         var index = this.view.getSelectedIndexes()[0];
47036         
47037         var r = this.store.getAt(index);
47038         
47039         if(r){
47040             this.onSelect(r, index);
47041         }
47042     },
47043     
47044     onSelect : function(record, index){
47045         
47046         if(this.fireEvent('beforeselect', this, record, index) !== false){
47047         
47048             this.setFromCurrencyData(index > -1 ? record.data : false);
47049             
47050             this.collapse();
47051             
47052             this.fireEvent('select', this, record, index);
47053         }
47054     },
47055     
47056     setFromCurrencyData : function(o)
47057     {
47058         var currency = '';
47059         
47060         this.lastCurrency = o;
47061         
47062         if (this.currencyField) {
47063             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
47064         } else {
47065             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
47066         }
47067         
47068         this.lastSelectionText = currency;
47069         
47070         //setting default currency
47071         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
47072             this.setCurrency(this.defaultCurrency);
47073             return;
47074         }
47075         
47076         this.setCurrency(currency);
47077     },
47078     
47079     setFromData : function(o)
47080     {
47081         var c = {};
47082         
47083         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
47084         
47085         this.setFromCurrencyData(c);
47086         
47087         var value = '';
47088         
47089         if (this.name) {
47090             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
47091         } else {
47092             Roo.log('no value set for '+ (this.name ? this.name : this.id));
47093         }
47094         
47095         this.setValue(value);
47096         
47097     },
47098     
47099     setCurrency : function(v)
47100     {   
47101         this.currencyValue = v;
47102         
47103         if(this.rendered){
47104             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
47105             this.validate();
47106         }
47107     },
47108     
47109     setValue : function(v)
47110     {
47111         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
47112         
47113         this.value = v;
47114         
47115         if(this.rendered){
47116             
47117             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
47118             
47119             this.inputEl().dom.value = (v == '') ? '' :
47120                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
47121             
47122             if(!this.allowZero && v === '0') {
47123                 this.hiddenEl().dom.value = '';
47124                 this.inputEl().dom.value = '';
47125             }
47126             
47127             this.validate();
47128         }
47129     },
47130     
47131     getRawValue : function()
47132     {
47133         var v = this.inputEl().getValue();
47134         
47135         return v;
47136     },
47137     
47138     getValue : function()
47139     {
47140         return this.fixPrecision(this.parseValue(this.getRawValue()));
47141     },
47142     
47143     parseValue : function(value)
47144     {
47145         if(this.thousandsDelimiter) {
47146             value += "";
47147             r = new RegExp(",", "g");
47148             value = value.replace(r, "");
47149         }
47150         
47151         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
47152         return isNaN(value) ? '' : value;
47153         
47154     },
47155     
47156     fixPrecision : function(value)
47157     {
47158         if(this.thousandsDelimiter) {
47159             value += "";
47160             r = new RegExp(",", "g");
47161             value = value.replace(r, "");
47162         }
47163         
47164         var nan = isNaN(value);
47165         
47166         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
47167             return nan ? '' : value;
47168         }
47169         return parseFloat(value).toFixed(this.decimalPrecision);
47170     },
47171     
47172     decimalPrecisionFcn : function(v)
47173     {
47174         return Math.floor(v);
47175     },
47176     
47177     validateValue : function(value)
47178     {
47179         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
47180             return false;
47181         }
47182         
47183         var num = this.parseValue(value);
47184         
47185         if(isNaN(num)){
47186             this.markInvalid(String.format(this.nanText, value));
47187             return false;
47188         }
47189         
47190         if(num < this.minValue){
47191             this.markInvalid(String.format(this.minText, this.minValue));
47192             return false;
47193         }
47194         
47195         if(num > this.maxValue){
47196             this.markInvalid(String.format(this.maxText, this.maxValue));
47197             return false;
47198         }
47199         
47200         return true;
47201     },
47202     
47203     validate : function()
47204     {
47205         if(this.disabled || this.allowBlank){
47206             this.markValid();
47207             return true;
47208         }
47209         
47210         var currency = this.getCurrency();
47211         
47212         if(this.validateValue(this.getRawValue()) && currency.length){
47213             this.markValid();
47214             return true;
47215         }
47216         
47217         this.markInvalid();
47218         return false;
47219     },
47220     
47221     getName: function()
47222     {
47223         return this.name;
47224     },
47225     
47226     beforeBlur : function()
47227     {
47228         if(!this.castInt){
47229             return;
47230         }
47231         
47232         var v = this.parseValue(this.getRawValue());
47233         
47234         if(v || v == 0){
47235             this.setValue(v);
47236         }
47237     },
47238     
47239     onBlur : function()
47240     {
47241         this.beforeBlur();
47242         
47243         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
47244             //this.el.removeClass(this.focusClass);
47245         }
47246         
47247         this.hasFocus = false;
47248         
47249         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
47250             this.validate();
47251         }
47252         
47253         var v = this.getValue();
47254         
47255         if(String(v) !== String(this.startValue)){
47256             this.fireEvent('change', this, v, this.startValue);
47257         }
47258         
47259         this.fireEvent("blur", this);
47260     },
47261     
47262     inputEl : function()
47263     {
47264         return this.el.select('.roo-money-amount-input', true).first();
47265     },
47266     
47267     currencyEl : function()
47268     {
47269         return this.el.select('.roo-money-currency-input', true).first();
47270     },
47271     
47272     hiddenEl : function()
47273     {
47274         return this.el.select('input.hidden-number-input',true).first();
47275     }
47276     
47277 });/**
47278  * @class Roo.bootstrap.BezierSignature
47279  * @extends Roo.bootstrap.Component
47280  * Bootstrap BezierSignature class
47281  * This script refer to:
47282  *    Title: Signature Pad
47283  *    Author: szimek
47284  *    Availability: https://github.com/szimek/signature_pad
47285  *
47286  * @constructor
47287  * Create a new BezierSignature
47288  * @param {Object} config The config object
47289  */
47290
47291 Roo.bootstrap.BezierSignature = function(config){
47292     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
47293     this.addEvents({
47294         "resize" : true
47295     });
47296 };
47297
47298 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
47299 {
47300      
47301     curve_data: [],
47302     
47303     is_empty: true,
47304     
47305     mouse_btn_down: true,
47306     
47307     /**
47308      * @cfg {int} canvas height
47309      */
47310     canvas_height: '200px',
47311     
47312     /**
47313      * @cfg {float|function} Radius of a single dot.
47314      */ 
47315     dot_size: false,
47316     
47317     /**
47318      * @cfg {float} Minimum width of a line. Defaults to 0.5.
47319      */
47320     min_width: 0.5,
47321     
47322     /**
47323      * @cfg {float} Maximum width of a line. Defaults to 2.5.
47324      */
47325     max_width: 2.5,
47326     
47327     /**
47328      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
47329      */
47330     throttle: 16,
47331     
47332     /**
47333      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
47334      */
47335     min_distance: 5,
47336     
47337     /**
47338      * @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.
47339      */
47340     bg_color: 'rgba(0, 0, 0, 0)',
47341     
47342     /**
47343      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
47344      */
47345     dot_color: 'black',
47346     
47347     /**
47348      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
47349      */ 
47350     velocity_filter_weight: 0.7,
47351     
47352     /**
47353      * @cfg {function} Callback when stroke begin. 
47354      */
47355     onBegin: false,
47356     
47357     /**
47358      * @cfg {function} Callback when stroke end.
47359      */
47360     onEnd: false,
47361     
47362     getAutoCreate : function()
47363     {
47364         var cls = 'roo-signature column';
47365         
47366         if(this.cls){
47367             cls += ' ' + this.cls;
47368         }
47369         
47370         var col_sizes = [
47371             'lg',
47372             'md',
47373             'sm',
47374             'xs'
47375         ];
47376         
47377         for(var i = 0; i < col_sizes.length; i++) {
47378             if(this[col_sizes[i]]) {
47379                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
47380             }
47381         }
47382         
47383         var cfg = {
47384             tag: 'div',
47385             cls: cls,
47386             cn: [
47387                 {
47388                     tag: 'div',
47389                     cls: 'roo-signature-body',
47390                     cn: [
47391                         {
47392                             tag: 'canvas',
47393                             cls: 'roo-signature-body-canvas',
47394                             height: this.canvas_height,
47395                             width: this.canvas_width
47396                         }
47397                     ]
47398                 },
47399                 {
47400                     tag: 'input',
47401                     type: 'file',
47402                     style: 'display: none'
47403                 }
47404             ]
47405         };
47406         
47407         return cfg;
47408     },
47409     
47410     initEvents: function() 
47411     {
47412         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
47413         
47414         var canvas = this.canvasEl();
47415         
47416         // mouse && touch event swapping...
47417         canvas.dom.style.touchAction = 'none';
47418         canvas.dom.style.msTouchAction = 'none';
47419         
47420         this.mouse_btn_down = false;
47421         canvas.on('mousedown', this._handleMouseDown, this);
47422         canvas.on('mousemove', this._handleMouseMove, this);
47423         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
47424         
47425         if (window.PointerEvent) {
47426             canvas.on('pointerdown', this._handleMouseDown, this);
47427             canvas.on('pointermove', this._handleMouseMove, this);
47428             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
47429         }
47430         
47431         if ('ontouchstart' in window) {
47432             canvas.on('touchstart', this._handleTouchStart, this);
47433             canvas.on('touchmove', this._handleTouchMove, this);
47434             canvas.on('touchend', this._handleTouchEnd, this);
47435         }
47436         
47437         Roo.EventManager.onWindowResize(this.resize, this, true);
47438         
47439         // file input event
47440         this.fileEl().on('change', this.uploadImage, this);
47441         
47442         this.clear();
47443         
47444         this.resize();
47445     },
47446     
47447     resize: function(){
47448         
47449         var canvas = this.canvasEl().dom;
47450         var ctx = this.canvasElCtx();
47451         var img_data = false;
47452         
47453         if(canvas.width > 0) {
47454             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
47455         }
47456         // setting canvas width will clean img data
47457         canvas.width = 0;
47458         
47459         var style = window.getComputedStyle ? 
47460             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
47461             
47462         var padding_left = parseInt(style.paddingLeft) || 0;
47463         var padding_right = parseInt(style.paddingRight) || 0;
47464         
47465         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
47466         
47467         if(img_data) {
47468             ctx.putImageData(img_data, 0, 0);
47469         }
47470     },
47471     
47472     _handleMouseDown: function(e)
47473     {
47474         if (e.browserEvent.which === 1) {
47475             this.mouse_btn_down = true;
47476             this.strokeBegin(e);
47477         }
47478     },
47479     
47480     _handleMouseMove: function (e)
47481     {
47482         if (this.mouse_btn_down) {
47483             this.strokeMoveUpdate(e);
47484         }
47485     },
47486     
47487     _handleMouseUp: function (e)
47488     {
47489         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
47490             this.mouse_btn_down = false;
47491             this.strokeEnd(e);
47492         }
47493     },
47494     
47495     _handleTouchStart: function (e) {
47496         
47497         e.preventDefault();
47498         if (e.browserEvent.targetTouches.length === 1) {
47499             // var touch = e.browserEvent.changedTouches[0];
47500             // this.strokeBegin(touch);
47501             
47502              this.strokeBegin(e); // assume e catching the correct xy...
47503         }
47504     },
47505     
47506     _handleTouchMove: function (e) {
47507         e.preventDefault();
47508         // var touch = event.targetTouches[0];
47509         // _this._strokeMoveUpdate(touch);
47510         this.strokeMoveUpdate(e);
47511     },
47512     
47513     _handleTouchEnd: function (e) {
47514         var wasCanvasTouched = e.target === this.canvasEl().dom;
47515         if (wasCanvasTouched) {
47516             e.preventDefault();
47517             // var touch = event.changedTouches[0];
47518             // _this._strokeEnd(touch);
47519             this.strokeEnd(e);
47520         }
47521     },
47522     
47523     reset: function () {
47524         this._lastPoints = [];
47525         this._lastVelocity = 0;
47526         this._lastWidth = (this.min_width + this.max_width) / 2;
47527         this.canvasElCtx().fillStyle = this.dot_color;
47528     },
47529     
47530     strokeMoveUpdate: function(e)
47531     {
47532         this.strokeUpdate(e);
47533         
47534         if (this.throttle) {
47535             this.throttleStroke(this.strokeUpdate, this.throttle);
47536         }
47537         else {
47538             this.strokeUpdate(e);
47539         }
47540     },
47541     
47542     strokeBegin: function(e)
47543     {
47544         var newPointGroup = {
47545             color: this.dot_color,
47546             points: []
47547         };
47548         
47549         if (typeof this.onBegin === 'function') {
47550             this.onBegin(e);
47551         }
47552         
47553         this.curve_data.push(newPointGroup);
47554         this.reset();
47555         this.strokeUpdate(e);
47556     },
47557     
47558     strokeUpdate: function(e)
47559     {
47560         var rect = this.canvasEl().dom.getBoundingClientRect();
47561         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
47562         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
47563         var lastPoints = lastPointGroup.points;
47564         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
47565         var isLastPointTooClose = lastPoint
47566             ? point.distanceTo(lastPoint) <= this.min_distance
47567             : false;
47568         var color = lastPointGroup.color;
47569         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
47570             var curve = this.addPoint(point);
47571             if (!lastPoint) {
47572                 this.drawDot({color: color, point: point});
47573             }
47574             else if (curve) {
47575                 this.drawCurve({color: color, curve: curve});
47576             }
47577             lastPoints.push({
47578                 time: point.time,
47579                 x: point.x,
47580                 y: point.y
47581             });
47582         }
47583     },
47584     
47585     strokeEnd: function(e)
47586     {
47587         this.strokeUpdate(e);
47588         if (typeof this.onEnd === 'function') {
47589             this.onEnd(e);
47590         }
47591     },
47592     
47593     addPoint:  function (point) {
47594         var _lastPoints = this._lastPoints;
47595         _lastPoints.push(point);
47596         if (_lastPoints.length > 2) {
47597             if (_lastPoints.length === 3) {
47598                 _lastPoints.unshift(_lastPoints[0]);
47599             }
47600             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
47601             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
47602             _lastPoints.shift();
47603             return curve;
47604         }
47605         return null;
47606     },
47607     
47608     calculateCurveWidths: function (startPoint, endPoint) {
47609         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
47610             (1 - this.velocity_filter_weight) * this._lastVelocity;
47611
47612         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
47613         var widths = {
47614             end: newWidth,
47615             start: this._lastWidth
47616         };
47617         
47618         this._lastVelocity = velocity;
47619         this._lastWidth = newWidth;
47620         return widths;
47621     },
47622     
47623     drawDot: function (_a) {
47624         var color = _a.color, point = _a.point;
47625         var ctx = this.canvasElCtx();
47626         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
47627         ctx.beginPath();
47628         this.drawCurveSegment(point.x, point.y, width);
47629         ctx.closePath();
47630         ctx.fillStyle = color;
47631         ctx.fill();
47632     },
47633     
47634     drawCurve: function (_a) {
47635         var color = _a.color, curve = _a.curve;
47636         var ctx = this.canvasElCtx();
47637         var widthDelta = curve.endWidth - curve.startWidth;
47638         var drawSteps = Math.floor(curve.length()) * 2;
47639         ctx.beginPath();
47640         ctx.fillStyle = color;
47641         for (var i = 0; i < drawSteps; i += 1) {
47642         var t = i / drawSteps;
47643         var tt = t * t;
47644         var ttt = tt * t;
47645         var u = 1 - t;
47646         var uu = u * u;
47647         var uuu = uu * u;
47648         var x = uuu * curve.startPoint.x;
47649         x += 3 * uu * t * curve.control1.x;
47650         x += 3 * u * tt * curve.control2.x;
47651         x += ttt * curve.endPoint.x;
47652         var y = uuu * curve.startPoint.y;
47653         y += 3 * uu * t * curve.control1.y;
47654         y += 3 * u * tt * curve.control2.y;
47655         y += ttt * curve.endPoint.y;
47656         var width = curve.startWidth + ttt * widthDelta;
47657         this.drawCurveSegment(x, y, width);
47658         }
47659         ctx.closePath();
47660         ctx.fill();
47661     },
47662     
47663     drawCurveSegment: function (x, y, width) {
47664         var ctx = this.canvasElCtx();
47665         ctx.moveTo(x, y);
47666         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
47667         this.is_empty = false;
47668     },
47669     
47670     clear: function()
47671     {
47672         var ctx = this.canvasElCtx();
47673         var canvas = this.canvasEl().dom;
47674         ctx.fillStyle = this.bg_color;
47675         ctx.clearRect(0, 0, canvas.width, canvas.height);
47676         ctx.fillRect(0, 0, canvas.width, canvas.height);
47677         this.curve_data = [];
47678         this.reset();
47679         this.is_empty = true;
47680     },
47681     
47682     fileEl: function()
47683     {
47684         return  this.el.select('input',true).first();
47685     },
47686     
47687     canvasEl: function()
47688     {
47689         return this.el.select('canvas',true).first();
47690     },
47691     
47692     canvasElCtx: function()
47693     {
47694         return this.el.select('canvas',true).first().dom.getContext('2d');
47695     },
47696     
47697     getImage: function(type)
47698     {
47699         if(this.is_empty) {
47700             return false;
47701         }
47702         
47703         // encryption ?
47704         return this.canvasEl().dom.toDataURL('image/'+type, 1);
47705     },
47706     
47707     drawFromImage: function(img_src)
47708     {
47709         var img = new Image();
47710         
47711         img.onload = function(){
47712             this.canvasElCtx().drawImage(img, 0, 0);
47713         }.bind(this);
47714         
47715         img.src = img_src;
47716         
47717         this.is_empty = false;
47718     },
47719     
47720     selectImage: function()
47721     {
47722         this.fileEl().dom.click();
47723     },
47724     
47725     uploadImage: function(e)
47726     {
47727         var reader = new FileReader();
47728         
47729         reader.onload = function(e){
47730             var img = new Image();
47731             img.onload = function(){
47732                 this.reset();
47733                 this.canvasElCtx().drawImage(img, 0, 0);
47734             }.bind(this);
47735             img.src = e.target.result;
47736         }.bind(this);
47737         
47738         reader.readAsDataURL(e.target.files[0]);
47739     },
47740     
47741     // Bezier Point Constructor
47742     Point: (function () {
47743         function Point(x, y, time) {
47744             this.x = x;
47745             this.y = y;
47746             this.time = time || Date.now();
47747         }
47748         Point.prototype.distanceTo = function (start) {
47749             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
47750         };
47751         Point.prototype.equals = function (other) {
47752             return this.x === other.x && this.y === other.y && this.time === other.time;
47753         };
47754         Point.prototype.velocityFrom = function (start) {
47755             return this.time !== start.time
47756             ? this.distanceTo(start) / (this.time - start.time)
47757             : 0;
47758         };
47759         return Point;
47760     }()),
47761     
47762     
47763     // Bezier Constructor
47764     Bezier: (function () {
47765         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
47766             this.startPoint = startPoint;
47767             this.control2 = control2;
47768             this.control1 = control1;
47769             this.endPoint = endPoint;
47770             this.startWidth = startWidth;
47771             this.endWidth = endWidth;
47772         }
47773         Bezier.fromPoints = function (points, widths, scope) {
47774             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
47775             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
47776             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
47777         };
47778         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
47779             var dx1 = s1.x - s2.x;
47780             var dy1 = s1.y - s2.y;
47781             var dx2 = s2.x - s3.x;
47782             var dy2 = s2.y - s3.y;
47783             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
47784             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
47785             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
47786             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
47787             var dxm = m1.x - m2.x;
47788             var dym = m1.y - m2.y;
47789             var k = l2 / (l1 + l2);
47790             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
47791             var tx = s2.x - cm.x;
47792             var ty = s2.y - cm.y;
47793             return {
47794                 c1: new scope.Point(m1.x + tx, m1.y + ty),
47795                 c2: new scope.Point(m2.x + tx, m2.y + ty)
47796             };
47797         };
47798         Bezier.prototype.length = function () {
47799             var steps = 10;
47800             var length = 0;
47801             var px;
47802             var py;
47803             for (var i = 0; i <= steps; i += 1) {
47804                 var t = i / steps;
47805                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
47806                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
47807                 if (i > 0) {
47808                     var xdiff = cx - px;
47809                     var ydiff = cy - py;
47810                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
47811                 }
47812                 px = cx;
47813                 py = cy;
47814             }
47815             return length;
47816         };
47817         Bezier.prototype.point = function (t, start, c1, c2, end) {
47818             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
47819             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
47820             + (3.0 * c2 * (1.0 - t) * t * t)
47821             + (end * t * t * t);
47822         };
47823         return Bezier;
47824     }()),
47825     
47826     throttleStroke: function(fn, wait) {
47827       if (wait === void 0) { wait = 250; }
47828       var previous = 0;
47829       var timeout = null;
47830       var result;
47831       var storedContext;
47832       var storedArgs;
47833       var later = function () {
47834           previous = Date.now();
47835           timeout = null;
47836           result = fn.apply(storedContext, storedArgs);
47837           if (!timeout) {
47838               storedContext = null;
47839               storedArgs = [];
47840           }
47841       };
47842       return function wrapper() {
47843           var args = [];
47844           for (var _i = 0; _i < arguments.length; _i++) {
47845               args[_i] = arguments[_i];
47846           }
47847           var now = Date.now();
47848           var remaining = wait - (now - previous);
47849           storedContext = this;
47850           storedArgs = args;
47851           if (remaining <= 0 || remaining > wait) {
47852               if (timeout) {
47853                   clearTimeout(timeout);
47854                   timeout = null;
47855               }
47856               previous = now;
47857               result = fn.apply(storedContext, storedArgs);
47858               if (!timeout) {
47859                   storedContext = null;
47860                   storedArgs = [];
47861               }
47862           }
47863           else if (!timeout) {
47864               timeout = window.setTimeout(later, remaining);
47865           }
47866           return result;
47867       };
47868   }
47869   
47870 });
47871
47872  
47873
47874  // old names for form elements
47875 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
47876 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
47877 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
47878 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
47879 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
47880 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
47881 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
47882 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
47883 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
47884 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
47885 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
47886 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
47887 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
47888 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
47889 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
47890 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
47891 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
47892 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
47893 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
47894 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
47895 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
47896 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
47897 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
47898 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
47899 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
47900 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
47901
47902 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
47903 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
47904
47905 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
47906 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
47907
47908 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
47909 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
47910 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
47911 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
47912