Roo/util/Observable.js
[roojs1] / roojs-bootstrap-debug.js
1 Roo.bootstrap = {};/**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); Roo.bootstrap.menu = Roo.bootstrap.menu || {};
18 Roo.bootstrap.nav = {};
19
20 Roo.bootstrap.form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {};/*
21  * Based on:
22  * Ext JS Library 1.1.1
23  * Copyright(c) 2006-2007, Ext JS, LLC.
24  *
25  * Originally Released Under LGPL - original licence link has changed is not relivant.
26  *
27  * Fork - LGPL
28  * <script type="text/javascript">
29  */
30
31
32 /**
33  * @class Roo.Shadow
34  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
35  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
36  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
37  * @constructor
38  * Create a new Shadow
39  * @param {Object} config The config object
40  */
41 Roo.Shadow = function(config){
42     Roo.apply(this, config);
43     if(typeof this.mode != "string"){
44         this.mode = this.defaultMode;
45     }
46     var o = this.offset, a = {h: 0};
47     var rad = Math.floor(this.offset/2);
48     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
49         case "drop":
50             a.w = 0;
51             a.l = a.t = o;
52             a.t -= 1;
53             if(Roo.isIE){
54                 a.l -= this.offset + rad;
55                 a.t -= this.offset + rad;
56                 a.w -= rad;
57                 a.h -= rad;
58                 a.t += 1;
59             }
60         break;
61         case "sides":
62             a.w = (o*2);
63             a.l = -o;
64             a.t = o-1;
65             if(Roo.isIE){
66                 a.l -= (this.offset - rad);
67                 a.t -= this.offset + rad;
68                 a.l += 1;
69                 a.w -= (this.offset - rad)*2;
70                 a.w -= rad + 1;
71                 a.h -= 1;
72             }
73         break;
74         case "frame":
75             a.w = a.h = (o*2);
76             a.l = a.t = -o;
77             a.t += 1;
78             a.h -= 2;
79             if(Roo.isIE){
80                 a.l -= (this.offset - rad);
81                 a.t -= (this.offset - rad);
82                 a.l += 1;
83                 a.w -= (this.offset + rad + 1);
84                 a.h -= (this.offset + rad);
85                 a.h += 1;
86             }
87         break;
88     };
89
90     this.adjusts = a;
91 };
92
93 Roo.Shadow.prototype = {
94     /**
95      * @cfg {String} mode
96      * The shadow display mode.  Supports the following options:<br />
97      * sides: Shadow displays on both sides and bottom only<br />
98      * frame: Shadow displays equally on all four sides<br />
99      * drop: Traditional bottom-right drop shadow (default)
100      */
101     mode: false,
102     /**
103      * @cfg {String} offset
104      * The number of pixels to offset the shadow from the element (defaults to 4)
105      */
106     offset: 4,
107
108     // private
109     defaultMode: "drop",
110
111     /**
112      * Displays the shadow under the target element
113      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
114      */
115     show : function(target){
116         target = Roo.get(target);
117         if(!this.el){
118             this.el = Roo.Shadow.Pool.pull();
119             if(this.el.dom.nextSibling != target.dom){
120                 this.el.insertBefore(target);
121             }
122         }
123         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
124         if(Roo.isIE){
125             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
126         }
127         this.realign(
128             target.getLeft(true),
129             target.getTop(true),
130             target.getWidth(),
131             target.getHeight()
132         );
133         this.el.dom.style.display = "block";
134     },
135
136     /**
137      * Returns true if the shadow is visible, else false
138      */
139     isVisible : function(){
140         return this.el ? true : false;  
141     },
142
143     /**
144      * Direct alignment when values are already available. Show must be called at least once before
145      * calling this method to ensure it is initialized.
146      * @param {Number} left The target element left position
147      * @param {Number} top The target element top position
148      * @param {Number} width The target element width
149      * @param {Number} height The target element height
150      */
151     realign : function(l, t, w, h){
152         if(!this.el){
153             return;
154         }
155         var a = this.adjusts, d = this.el.dom, s = d.style;
156         var iea = 0;
157         s.left = (l+a.l)+"px";
158         s.top = (t+a.t)+"px";
159         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
160  
161         if(s.width != sws || s.height != shs){
162             s.width = sws;
163             s.height = shs;
164             if(!Roo.isIE){
165                 var cn = d.childNodes;
166                 var sww = Math.max(0, (sw-12))+"px";
167                 cn[0].childNodes[1].style.width = sww;
168                 cn[1].childNodes[1].style.width = sww;
169                 cn[2].childNodes[1].style.width = sww;
170                 cn[1].style.height = Math.max(0, (sh-12))+"px";
171             }
172         }
173     },
174
175     /**
176      * Hides this shadow
177      */
178     hide : function(){
179         if(this.el){
180             this.el.dom.style.display = "none";
181             Roo.Shadow.Pool.push(this.el);
182             delete this.el;
183         }
184     },
185
186     /**
187      * Adjust the z-index of this shadow
188      * @param {Number} zindex The new z-index
189      */
190     setZIndex : function(z){
191         this.zIndex = z;
192         if(this.el){
193             this.el.setStyle("z-index", z);
194         }
195     }
196 };
197
198 // Private utility class that manages the internal Shadow cache
199 Roo.Shadow.Pool = function(){
200     var p = [];
201     var markup = Roo.isIE ?
202                  '<div class="x-ie-shadow"></div>' :
203                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204     return {
205         pull : function(){
206             var sh = p.shift();
207             if(!sh){
208                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
209                 sh.autoBoxAdjust = false;
210             }
211             return sh;
212         },
213
214         push : function(sh){
215             p.push(sh);
216         }
217     };
218 }();/*
219  * - LGPL
220  *
221  * base class for bootstrap elements.
222  * 
223  */
224
225 Roo.bootstrap = Roo.bootstrap || {};
226 /**
227  * @class Roo.bootstrap.Component
228  * @extends Roo.Component
229  * @abstract
230  * @children Roo.bootstrap.Component
231  * Bootstrap Component base class
232  * @cfg {String} cls css class
233  * @cfg {String} style any extra css
234  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
235  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
236  * @cfg {string} dataId cutomer id
237  * @cfg {string} name Specifies name attribute
238  * @cfg {string} tooltip  Text for the tooltip
239  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
240  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
241  
242  * @constructor
243  * Do not use directly - it does not do anything..
244  * @param {Object} config The config object
245  */
246
247
248
249 Roo.bootstrap.Component = function(config){
250     console.log("BOOSTRAP COMPONENT CONSTRUCTOR");
251     console.log(config);
252     Roo.bootstrap.Component.superclass.constructor.call(this, config);
253        
254     this.addEvents({
255         /**
256          * @event childrenrendered
257          * Fires when the children have been rendered..
258          * @param {Roo.bootstrap.Component} this
259          */
260         "childrenrendered" : true
261         
262         
263         
264     });
265     
266     
267 };
268
269 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
270     
271     
272     allowDomMove : false, // to stop relocations in parent onRender...
273     
274     cls : false,
275     
276     style : false,
277     
278     autoCreate : false,
279     
280     tooltip : null,
281     /**
282      * Initialize Events for the element
283      */
284     initEvents : function() { },
285     
286     xattr : false,
287     
288     parentId : false,
289     
290     can_build_overlaid : true,
291     
292     container_method : false,
293     
294     dataId : false,
295     
296     name : false,
297     
298     parent: function() {
299         // returns the parent component..
300         return Roo.ComponentMgr.get(this.parentId)
301         
302         
303     },
304     
305     // private
306     onRender : function(ct, position)
307     {
308        // Roo.log("Call onRender: " + this.xtype);
309         
310         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
311         
312         if(this.el){
313             if (this.el.attr('xtype')) {
314                 this.el.attr('xtypex', this.el.attr('xtype'));
315                 this.el.dom.removeAttribute('xtype');
316                 
317                 this.initEvents();
318             }
319             
320             return;
321         }
322         
323          
324         
325         var cfg = Roo.apply({},  this.getAutoCreate());
326         
327         cfg.id = this.id || Roo.id();
328         
329         // fill in the extra attributes 
330         if (this.xattr && typeof(this.xattr) =='object') {
331             for (var i in this.xattr) {
332                 cfg[i] = this.xattr[i];
333             }
334         }
335         
336         if(this.dataId){
337             cfg.dataId = this.dataId;
338         }
339         
340         if (this.cls) {
341             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
342         }
343         
344         if (this.style) { // fixme needs to support more complex style data.
345             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
346         }
347         
348         if(this.name){
349             cfg.name = this.name;
350         }
351         
352         this.el = ct.createChild(cfg, position);
353         
354         if (this.tooltip) {
355             this.tooltipEl().attr('tooltip', this.tooltip);
356         }
357         
358         if(this.tabIndex !== undefined){
359             this.el.dom.setAttribute('tabIndex', this.tabIndex);
360         }
361         
362         this.initEvents();
363         
364     },
365     /**
366      * Fetch the element to add children to
367      * @return {Roo.Element} defaults to this.el
368      */
369     getChildContainer : function()
370     {
371         return this.el;
372     },
373     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
374     {
375         return Roo.get(document.body);
376     },
377     
378     /**
379      * Fetch the element to display the tooltip on.
380      * @return {Roo.Element} defaults to this.el
381      */
382     tooltipEl : function()
383     {
384         return this.el;
385     },
386         
387     addxtype  : function(tree,cntr)
388     {
389         var cn = this;
390         
391         cn = Roo.factory(tree);
392         //Roo.log(['addxtype', cn]);
393            
394         cn.parentType = this.xtype; //??
395         cn.parentId = this.id;
396         
397         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
398         if (typeof(cn.container_method) == 'string') {
399             cntr = cn.container_method;
400         }
401         
402         
403         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
404         
405         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
406         
407         var build_from_html =  Roo.XComponent.build_from_html;
408           
409         var is_body  = (tree.xtype == 'Body') ;
410           
411         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
412           
413         var self_cntr_el = Roo.get(this[cntr](false));
414         
415         // do not try and build conditional elements 
416         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
417             return false;
418         }
419         
420         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
421             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
422                 return this.addxtypeChild(tree,cntr, is_body);
423             }
424             
425             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
426                 
427             if(echild){
428                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
429             }
430             
431             Roo.log('skipping render');
432             return cn;
433             
434         }
435         
436         var ret = false;
437         if (!build_from_html) {
438             return false;
439         }
440         
441         // this i think handles overlaying multiple children of the same type
442         // with the sam eelement.. - which might be buggy..
443         while (true) {
444             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
445             
446             if (!echild) {
447                 break;
448             }
449             
450             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
451                 break;
452             }
453             
454             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
455         }
456        
457         return ret;
458     },
459     
460     
461     addxtypeChild : function (tree, cntr, is_body)
462     {
463         Roo.debug && Roo.log('addxtypeChild:' + cntr);
464         var cn = this;
465         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
466         
467         
468         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
469                     (typeof(tree['flexy:foreach']) != 'undefined');
470           
471     
472         
473         skip_children = false;
474         // render the element if it's not BODY.
475         if (!is_body) {
476             
477             // if parent was disabled, then do not try and create the children..
478             if(!this[cntr](true)){
479                 tree.items = [];
480                 return tree;
481             }
482            
483             cn = Roo.factory(tree);
484            
485             cn.parentType = this.xtype; //??
486             cn.parentId = this.id;
487             
488             var build_from_html =  Roo.XComponent.build_from_html;
489             
490             
491             // does the container contain child eleemnts with 'xtype' attributes.
492             // that match this xtype..
493             // note - when we render we create these as well..
494             // so we should check to see if body has xtype set.
495             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
496                
497                 var self_cntr_el = Roo.get(this[cntr](false));
498                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
499                 if (echild) { 
500                     //Roo.log(Roo.XComponent.build_from_html);
501                     //Roo.log("got echild:");
502                     //Roo.log(echild);
503                 }
504                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
505                 // and are not displayed -this causes this to use up the wrong element when matching.
506                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
507                 
508                 
509                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
510                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
511                   
512                   
513                   
514                     cn.el = echild;
515                   //  Roo.log("GOT");
516                     //echild.dom.removeAttribute('xtype');
517                 } else {
518                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
519                     Roo.debug && Roo.log(self_cntr_el);
520                     Roo.debug && Roo.log(echild);
521                     Roo.debug && Roo.log(cn);
522                 }
523             }
524            
525             
526            
527             // if object has flexy:if - then it may or may not be rendered.
528             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
529                 // skip a flexy if element.
530                 Roo.debug && Roo.log('skipping render');
531                 Roo.debug && Roo.log(tree);
532                 if (!cn.el) {
533                     Roo.debug && Roo.log('skipping all children');
534                     skip_children = true;
535                 }
536                 
537              } else {
538                  
539                 // actually if flexy:foreach is found, we really want to create 
540                 // multiple copies here...
541                 //Roo.log('render');
542                 //Roo.log(this[cntr]());
543                 // some elements do not have render methods.. like the layouts...
544                 /*
545                 if(this[cntr](true) === false){
546                     cn.items = [];
547                     return cn;
548                 }
549                 */
550                 cn.render && cn.render(this[cntr](true));
551                 
552              }
553             // then add the element..
554         }
555          
556         // handle the kids..
557         
558         var nitems = [];
559         /*
560         if (typeof (tree.menu) != 'undefined') {
561             tree.menu.parentType = cn.xtype;
562             tree.menu.triggerEl = cn.el;
563             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
564             
565         }
566         */
567         if (!tree.items || !tree.items.length) {
568             cn.items = nitems;
569             //Roo.log(["no children", this]);
570             
571             return cn;
572         }
573          
574         var items = tree.items;
575         delete tree.items;
576         
577         //Roo.log(items.length);
578             // add the items..
579         if (!skip_children) {    
580             for(var i =0;i < items.length;i++) {
581               //  Roo.log(['add child', items[i]]);
582                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
583             }
584         }
585         
586         cn.items = nitems;
587         
588         //Roo.log("fire childrenrendered");
589         
590         cn.fireEvent('childrenrendered', this);
591         
592         return cn;
593     },
594     
595     /**
596      * Set the element that will be used to show or hide
597      */
598     setVisibilityEl : function(el)
599     {
600         this.visibilityEl = el;
601     },
602     
603      /**
604      * Get the element that will be used to show or hide
605      */
606     getVisibilityEl : function()
607     {
608         if (typeof(this.visibilityEl) == 'object') {
609             return this.visibilityEl;
610         }
611         
612         if (typeof(this.visibilityEl) == 'string') {
613             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
614         }
615         
616         return this.getEl();
617     },
618     
619     /**
620      * Show a component - removes 'hidden' class
621      */
622     show : function()
623     {
624         if(!this.getVisibilityEl()){
625             return;
626         }
627          
628         this.getVisibilityEl().removeClass(['hidden','d-none']);
629         
630         this.fireEvent('show', this);
631         
632         
633     },
634     /**
635      * Hide a component - adds 'hidden' class
636      */
637     hide: function()
638     {
639         if(!this.getVisibilityEl()){
640             return;
641         }
642         
643         this.getVisibilityEl().addClass(['hidden','d-none']);
644         
645         this.fireEvent('hide', this);
646         
647     }
648 });
649
650  /*
651  * - LGPL
652  *
653  * element
654  * 
655  */
656
657 /**
658  * @class Roo.bootstrap.Element
659  * @extends Roo.bootstrap.Component
660  * @children Roo.bootstrap.Component
661  * Bootstrap Element class (basically a DIV used to make random stuff )
662  * 
663  * @cfg {String} html contents of the element
664  * @cfg {String} tag tag of the element
665  * @cfg {String} cls class of the element
666  * @cfg {Boolean} preventDefault (true|false) default false
667  * @cfg {Boolean} clickable (true|false) default false
668  * @cfg {String} role default blank - set to button to force cursor pointer
669  
670  * 
671  * @constructor
672  * Create a new Element
673  * @param {Object} config The config object
674  */
675
676 Roo.bootstrap.Element = function(config){
677     Roo.bootstrap.Element.superclass.constructor.call(this, config);
678     
679     this.addEvents({
680         // raw events
681         /**
682          * @event click
683          * When a element is chick
684          * @param {Roo.bootstrap.Element} this
685          * @param {Roo.EventObject} e
686          */
687         "click" : true 
688         
689       
690     });
691 };
692
693 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
694     
695     tag: 'div',
696     cls: '',
697     html: '',
698     preventDefault: false, 
699     clickable: false,
700     tapedTwice : false,
701     role : false,
702     
703     getAutoCreate : function(){
704         
705         var cfg = {
706             tag: this.tag,
707             // cls: this.cls, double assign in parent class Component.js :: onRender
708             html: this.html
709         };
710         if (this.role !== false) {
711             cfg.role = this.role;
712         }
713         
714         return cfg;
715     },
716     
717     initEvents: function() 
718     {
719         Roo.bootstrap.Element.superclass.initEvents.call(this);
720         
721         if(this.clickable){
722             this.el.on('click', this.onClick, this);
723         }
724         
725         
726     },
727     
728     onClick : function(e)
729     {
730         if(this.preventDefault){
731             e.preventDefault();
732         }
733         
734         this.fireEvent('click', this, e); // why was this double click before?
735     },
736     
737     
738     
739
740     
741     
742     getValue : function()
743     {
744         return this.el.dom.innerHTML;
745     },
746     
747     setValue : function(value)
748     {
749         this.el.dom.innerHTML = value;
750     }
751    
752 });
753
754  
755
756  /*
757  * - LGPL
758  *
759  * dropable area
760  * 
761  */
762
763 /**
764  * @class Roo.bootstrap.DropTarget
765  * @extends Roo.bootstrap.Element
766  * Bootstrap DropTarget class
767  
768  * @cfg {string} name dropable name
769  * 
770  * @constructor
771  * Create a new Dropable Area
772  * @param {Object} config The config object
773  */
774
775 Roo.bootstrap.DropTarget = function(config){
776     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
777     
778     this.addEvents({
779         // raw events
780         /**
781          * @event click
782          * When a element is chick
783          * @param {Roo.bootstrap.Element} this
784          * @param {Roo.EventObject} e
785          */
786         "drop" : true
787     });
788 };
789
790 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
791     
792     
793     getAutoCreate : function(){
794         
795          
796     },
797     
798     initEvents: function() 
799     {
800         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
801         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
802             ddGroup: this.name,
803             listeners : {
804                 drop : this.dragDrop.createDelegate(this),
805                 enter : this.dragEnter.createDelegate(this),
806                 out : this.dragOut.createDelegate(this),
807                 over : this.dragOver.createDelegate(this)
808             }
809             
810         });
811         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
812     },
813     
814     dragDrop : function(source,e,data)
815     {
816         // user has to decide how to impliment this.
817         Roo.log('drop');
818         Roo.log(this);
819         //this.fireEvent('drop', this, source, e ,data);
820         return false;
821     },
822     
823     dragEnter : function(n, dd, e, data)
824     {
825         // probably want to resize the element to match the dropped element..
826         Roo.log("enter");
827         this.originalSize = this.el.getSize();
828         this.el.setSize( n.el.getSize());
829         this.dropZone.DDM.refreshCache(this.name);
830         Roo.log([n, dd, e, data]);
831     },
832     
833     dragOut : function(value)
834     {
835         // resize back to normal
836         Roo.log("out");
837         this.el.setSize(this.originalSize);
838         this.dropZone.resetConstraints();
839     },
840     
841     dragOver : function()
842     {
843         // ??? do nothing?
844     }
845    
846 });
847
848  
849
850  /*
851  * - LGPL
852  *
853  * Body
854  *
855  */
856
857 /**
858  * @class Roo.bootstrap.Body
859  * @extends Roo.bootstrap.Component
860  * @children Roo.bootstrap.Component 
861  * @parent none builder
862  * Bootstrap Body class
863  *
864  * @constructor
865  * Create a new body
866  * @param {Object} config The config object
867  */
868
869 Roo.bootstrap.Body = function(config){
870
871     config = config || {};
872
873     Roo.bootstrap.Body.superclass.constructor.call(this, config);
874     this.el = Roo.get(config.el ? config.el : document.body );
875     if (this.cls && this.cls.length) {
876         Roo.get(document.body).addClass(this.cls);
877     }
878 };
879
880 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
881
882     is_body : true,// just to make sure it's constructed?
883
884         autoCreate : {
885         cls: 'container'
886     },
887     onRender : function(ct, position)
888     {
889        /* Roo.log("Roo.bootstrap.Body - onRender");
890         if (this.cls && this.cls.length) {
891             Roo.get(document.body).addClass(this.cls);
892         }
893         // style??? xttr???
894         */
895     }
896
897
898
899
900 });
901 /*
902  * - LGPL
903  *
904  * button group
905  * 
906  */
907
908
909 /**
910  * @class Roo.bootstrap.ButtonGroup
911  * @extends Roo.bootstrap.Component
912  * Bootstrap ButtonGroup class
913  * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
914  * 
915  * @cfg {String} size lg | sm | xs (default empty normal)
916  * @cfg {String} align vertical | justified  (default none)
917  * @cfg {String} direction up | down (default down)
918  * @cfg {Boolean} toolbar false | true
919  * @cfg {Boolean} btn true | false
920  * 
921  * 
922  * @constructor
923  * Create a new Input
924  * @param {Object} config The config object
925  */
926
927 Roo.bootstrap.ButtonGroup = function(config){
928     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
929 };
930
931 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
932     
933     size: '',
934     align: '',
935     direction: '',
936     toolbar: false,
937     btn: true,
938
939     getAutoCreate : function(){
940         var cfg = {
941             cls: 'btn-group',
942             html : null
943         };
944         
945         cfg.html = this.html || cfg.html;
946         
947         if (this.toolbar) {
948             cfg = {
949                 cls: 'btn-toolbar',
950                 html: null
951             };
952             
953             return cfg;
954         }
955         
956         if (['vertical','justified'].indexOf(this.align)!==-1) {
957             cfg.cls = 'btn-group-' + this.align;
958             
959             if (this.align == 'justified') {
960                 console.log(this.items);
961             }
962         }
963         
964         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
965             cfg.cls += ' btn-group-' + this.size;
966         }
967         
968         if (this.direction == 'up') {
969             cfg.cls += ' dropup' ;
970         }
971         
972         return cfg;
973     },
974     /**
975      * Add a button to the group (similar to NavItem API.)
976      */
977     addItem : function(cfg)
978     {
979         var cn = new Roo.bootstrap.Button(cfg);
980         //this.register(cn);
981         cn.parentId = this.id;
982         cn.onRender(this.el, null);
983         return cn;
984     }
985    
986 });
987
988  /*
989  * - LGPL
990  *
991  * button
992  * 
993  */
994
995 /**
996  * @class Roo.bootstrap.Button
997  * @extends Roo.bootstrap.Component
998  * Bootstrap Button class
999  * @cfg {String} html The button content
1000  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
1001  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1002  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1003  * @cfg {String} size (lg|sm|xs)
1004  * @cfg {String} tag (a|input|submit)
1005  * @cfg {String} href empty or href
1006  * @cfg {Boolean} disabled default false;
1007  * @cfg {Boolean} isClose default false;
1008  * @cfg {String} glyphicon depricated - use fa
1009  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1010  * @cfg {String} badge text for badge
1011  * @cfg {String} theme (default|glow)  
1012  * @cfg {Boolean} inverse dark themed version
1013  * @cfg {Boolean} toggle is it a slidy toggle button
1014  * @cfg {Boolean} pressed   default null - if the button ahs active state
1015  * @cfg {String} ontext text for on slidy toggle state
1016  * @cfg {String} offtext text for off slidy toggle state
1017  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1018  * @cfg {Boolean} removeClass remove the standard class..
1019  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1020  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1021  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
1022
1023  * @constructor
1024  * Create a new button
1025  * @param {Object} config The config object
1026  */
1027
1028
1029 Roo.bootstrap.Button = function(config){
1030     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1031     
1032     this.addEvents({
1033         // raw events
1034         /**
1035          * @event click
1036          * When a button is pressed
1037          * @param {Roo.bootstrap.Button} btn
1038          * @param {Roo.EventObject} e
1039          */
1040         "click" : true,
1041         /**
1042          * @event dblclick
1043          * When a button is double clicked
1044          * @param {Roo.bootstrap.Button} btn
1045          * @param {Roo.EventObject} e
1046          */
1047         "dblclick" : true,
1048          /**
1049          * @event toggle
1050          * After the button has been toggles
1051          * @param {Roo.bootstrap.Button} btn
1052          * @param {Roo.EventObject} e
1053          * @param {boolean} pressed (also available as button.pressed)
1054          */
1055         "toggle" : true
1056     });
1057 };
1058
1059 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1060     html: false,
1061     active: false,
1062     weight: '',
1063     badge_weight: '',
1064     outline : false,
1065     size: '',
1066     tag: 'button',
1067     href: '',
1068     disabled: false,
1069     isClose: false,
1070     glyphicon: '',
1071     fa: '',
1072     badge: '',
1073     theme: 'default',
1074     inverse: false,
1075     
1076     toggle: false,
1077     ontext: 'ON',
1078     offtext: 'OFF',
1079     defaulton: true,
1080     preventDefault: true,
1081     removeClass: false,
1082     name: false,
1083     target: false,
1084     group : false,
1085      
1086     pressed : null,
1087      
1088     
1089     getAutoCreate : function(){
1090         
1091         var cfg = {
1092             tag : 'button',
1093             cls : 'roo-button',
1094             html: ''
1095         };
1096         
1097         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1098             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1099             this.tag = 'button';
1100         } else {
1101             cfg.tag = this.tag;
1102         }
1103         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1104         
1105         if (this.toggle == true) {
1106             cfg={
1107                 tag: 'div',
1108                 cls: 'slider-frame roo-button',
1109                 cn: [
1110                     {
1111                         tag: 'span',
1112                         'data-on-text':'ON',
1113                         'data-off-text':'OFF',
1114                         cls: 'slider-button',
1115                         html: this.offtext
1116                     }
1117                 ]
1118             };
1119             // why are we validating the weights?
1120             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1121                 cfg.cls +=  ' ' + this.weight;
1122             }
1123             
1124             return cfg;
1125         }
1126         
1127         if (this.isClose) {
1128             cfg.cls += ' close';
1129             
1130             cfg["aria-hidden"] = true;
1131             
1132             cfg.html = "&times;";
1133             
1134             return cfg;
1135         }
1136              
1137         
1138         if (this.theme==='default') {
1139             cfg.cls = 'btn roo-button';
1140             
1141             //if (this.parentType != 'Navbar') {
1142             this.weight = this.weight.length ?  this.weight : 'default';
1143             //}
1144             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1145                 
1146                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1147                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1148                 cfg.cls += ' btn-' + outline + weight;
1149                 if (this.weight == 'default') {
1150                     // BC
1151                     cfg.cls += ' btn-' + this.weight;
1152                 }
1153             }
1154         } else if (this.theme==='glow') {
1155             
1156             cfg.tag = 'a';
1157             cfg.cls = 'btn-glow roo-button';
1158             
1159             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1160                 
1161                 cfg.cls += ' ' + this.weight;
1162             }
1163         }
1164    
1165         
1166         if (this.inverse) {
1167             this.cls += ' inverse';
1168         }
1169         
1170         
1171         if (this.active || this.pressed === true) {
1172             cfg.cls += ' active';
1173         }
1174         
1175         if (this.disabled) {
1176             cfg.disabled = 'disabled';
1177         }
1178         
1179         if (this.items) {
1180             Roo.log('changing to ul' );
1181             cfg.tag = 'ul';
1182             this.glyphicon = 'caret';
1183             if (Roo.bootstrap.version == 4) {
1184                 this.fa = 'caret-down';
1185             }
1186             
1187         }
1188         
1189         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1190          
1191         //gsRoo.log(this.parentType);
1192         if (this.parentType === 'Navbar' && !this.parent().bar) {
1193             Roo.log('changing to li?');
1194             
1195             cfg.tag = 'li';
1196             
1197             cfg.cls = '';
1198             cfg.cn =  [{
1199                 tag : 'a',
1200                 cls : 'roo-button',
1201                 html : this.html,
1202                 href : this.href || '#'
1203             }];
1204             if (this.menu) {
1205                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1206                 cfg.cls += ' dropdown';
1207             }   
1208             
1209             delete cfg.html;
1210             
1211         }
1212         
1213        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1214         
1215         if (this.glyphicon) {
1216             cfg.html = ' ' + cfg.html;
1217             
1218             cfg.cn = [
1219                 {
1220                     tag: 'span',
1221                     cls: 'glyphicon glyphicon-' + this.glyphicon
1222                 }
1223             ];
1224         }
1225         if (this.fa) {
1226             cfg.html = ' ' + cfg.html;
1227             
1228             cfg.cn = [
1229                 {
1230                     tag: 'i',
1231                     cls: 'fa fas fa-' + this.fa
1232                 }
1233             ];
1234         }
1235         
1236         if (this.badge) {
1237             cfg.html += ' ';
1238             
1239             cfg.tag = 'a';
1240             
1241 //            cfg.cls='btn roo-button';
1242             
1243             cfg.href=this.href;
1244             
1245             var value = cfg.html;
1246             
1247             if(this.glyphicon){
1248                 value = {
1249                     tag: 'span',
1250                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1251                     html: this.html
1252                 };
1253             }
1254             if(this.fa){
1255                 value = {
1256                     tag: 'i',
1257                     cls: 'fa fas fa-' + this.fa,
1258                     html: this.html
1259                 };
1260             }
1261             
1262             var bw = this.badge_weight.length ? this.badge_weight :
1263                 (this.weight.length ? this.weight : 'secondary');
1264             bw = bw == 'default' ? 'secondary' : bw;
1265             
1266             cfg.cn = [
1267                 value,
1268                 {
1269                     tag: 'span',
1270                     cls: 'badge badge-' + bw,
1271                     html: this.badge
1272                 }
1273             ];
1274             
1275             cfg.html='';
1276         }
1277         
1278         if (this.menu) {
1279             cfg.cls += ' dropdown';
1280             cfg.html = typeof(cfg.html) != 'undefined' ?
1281                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1282         }
1283         
1284         if (cfg.tag !== 'a' && this.href !== '') {
1285             throw "Tag must be a to set href.";
1286         } else if (this.href.length > 0) {
1287             cfg.href = this.href;
1288         }
1289         
1290         if(this.removeClass){
1291             cfg.cls = '';
1292         }
1293         
1294         if(this.target){
1295             cfg.target = this.target;
1296         }
1297         
1298         return cfg;
1299     },
1300     initEvents: function() {
1301        // Roo.log('init events?');
1302 //        Roo.log(this.el.dom);
1303         // add the menu...
1304         
1305         if (typeof (this.menu) != 'undefined') {
1306             this.menu.parentType = this.xtype;
1307             this.menu.triggerEl = this.el;
1308             this.addxtype(Roo.apply({}, this.menu));
1309         }
1310
1311
1312         if (this.el.hasClass('roo-button')) {
1313              this.el.on('click', this.onClick, this);
1314              this.el.on('dblclick', this.onDblClick, this);
1315         } else {
1316              this.el.select('.roo-button').on('click', this.onClick, this);
1317              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1318              
1319         }
1320         // why?
1321         if(this.removeClass){
1322             this.el.on('click', this.onClick, this);
1323         }
1324         
1325         if (this.group === true) {
1326              if (this.pressed === false || this.pressed === true) {
1327                 // nothing
1328             } else {
1329                 this.pressed = false;
1330                 this.setActive(this.pressed);
1331             }
1332             
1333         }
1334         
1335         this.el.enableDisplayMode();
1336         
1337     },
1338     onClick : function(e)
1339     {
1340         if (this.disabled) {
1341             return;
1342         }
1343         
1344         Roo.log('button on click ');
1345         if(this.href === '' || this.preventDefault){
1346             e.preventDefault();
1347         }
1348         
1349         if (this.group) {
1350             if (this.pressed) {
1351                 // do nothing -
1352                 return;
1353             }
1354             this.setActive(true);
1355             var pi = this.parent().items;
1356             for (var i = 0;i < pi.length;i++) {
1357                 if (this == pi[i]) {
1358                     continue;
1359                 }
1360                 if (pi[i].el.hasClass('roo-button')) {
1361                     pi[i].setActive(false);
1362                 }
1363             }
1364             this.fireEvent('click', this, e);            
1365             return;
1366         }
1367         
1368         if (this.pressed === true || this.pressed === false) {
1369             this.toggleActive(e);
1370         }
1371         
1372         
1373         this.fireEvent('click', this, e);
1374     },
1375     onDblClick: function(e)
1376     {
1377         if (this.disabled) {
1378             return;
1379         }
1380         if(this.preventDefault){
1381             e.preventDefault();
1382         }
1383         this.fireEvent('dblclick', this, e);
1384     },
1385     /**
1386      * Enables this button
1387      */
1388     enable : function()
1389     {
1390         this.disabled = false;
1391         this.el.removeClass('disabled');
1392         this.el.dom.removeAttribute("disabled");
1393     },
1394     
1395     /**
1396      * Disable this button
1397      */
1398     disable : function()
1399     {
1400         this.disabled = true;
1401         this.el.addClass('disabled');
1402         this.el.attr("disabled", "disabled")
1403     },
1404      /**
1405      * sets the active state on/off, 
1406      * @param {Boolean} state (optional) Force a particular state
1407      */
1408     setActive : function(v) {
1409         
1410         this.el[v ? 'addClass' : 'removeClass']('active');
1411         this.pressed = v;
1412     },
1413      /**
1414      * toggles the current active state 
1415      */
1416     toggleActive : function(e)
1417     {
1418         this.setActive(!this.pressed); // this modifies pressed...
1419         this.fireEvent('toggle', this, e, this.pressed);
1420     },
1421      /**
1422      * get the current active state
1423      * @return {boolean} true if it's active
1424      */
1425     isActive : function()
1426     {
1427         return this.el.hasClass('active');
1428     },
1429     /**
1430      * set the text of the first selected button
1431      */
1432     setText : function(str)
1433     {
1434         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1435     },
1436     /**
1437      * get the text of the first selected button
1438      */
1439     getText : function()
1440     {
1441         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1442     },
1443     
1444     setWeight : function(str)
1445     {
1446         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1447         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1448         this.weight = str;
1449         var outline = this.outline ? 'outline-' : '';
1450         if (str == 'default') {
1451             this.el.addClass('btn-default btn-outline-secondary');        
1452             return;
1453         }
1454         this.el.addClass('btn-' + outline + str);        
1455     }
1456     
1457     
1458 });
1459 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1460
1461 Roo.bootstrap.Button.weights = [
1462     'default',
1463     'secondary' ,
1464     'primary',
1465     'success',
1466     'info',
1467     'warning',
1468     'danger',
1469     'link',
1470     'light',
1471     'dark'              
1472    
1473 ];/*
1474  * - LGPL
1475  *
1476  * column
1477  * 
1478  */
1479
1480 /**
1481  * @class Roo.bootstrap.Column
1482  * @extends Roo.bootstrap.Component
1483  * @children Roo.bootstrap.Component
1484  * Bootstrap Column class
1485  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1486  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1487  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1488  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1489  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1490  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1491  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1492  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1493  *
1494  * 
1495  * @cfg {Boolean} hidden (true|false) hide the element
1496  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1497  * @cfg {String} fa (ban|check|...) font awesome icon
1498  * @cfg {Number} fasize (1|2|....) font awsome size
1499
1500  * @cfg {String} icon (info-sign|check|...) glyphicon name
1501
1502  * @cfg {String} html content of column.
1503  * 
1504  * @constructor
1505  * Create a new Column
1506  * @param {Object} config The config object
1507  */
1508
1509 Roo.bootstrap.Column = function(config){
1510     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1511 };
1512
1513 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1514     
1515     xs: false,
1516     sm: false,
1517     md: false,
1518     lg: false,
1519     xsoff: false,
1520     smoff: false,
1521     mdoff: false,
1522     lgoff: false,
1523     html: '',
1524     offset: 0,
1525     alert: false,
1526     fa: false,
1527     icon : false,
1528     hidden : false,
1529     fasize : 1,
1530     
1531     getAutoCreate : function(){
1532         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1533         
1534         cfg = {
1535             tag: 'div',
1536             cls: 'column'
1537         };
1538         
1539         var settings=this;
1540         var sizes =   ['xs','sm','md','lg'];
1541         sizes.map(function(size ,ix){
1542             //Roo.log( size + ':' + settings[size]);
1543             
1544             if (settings[size+'off'] !== false) {
1545                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1546             }
1547             
1548             if (settings[size] === false) {
1549                 return;
1550             }
1551             
1552             if (!settings[size]) { // 0 = hidden
1553                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1554                 // bootsrap4
1555                 for (var i = ix; i > -1; i--) {
1556                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1557                 }
1558                 
1559                 
1560                 return;
1561             }
1562             cfg.cls += ' col-' + size + '-' + settings[size] + (
1563                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1564             );
1565             
1566         });
1567         
1568         if (this.hidden) {
1569             cfg.cls += ' hidden';
1570         }
1571         
1572         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1573             cfg.cls +=' alert alert-' + this.alert;
1574         }
1575         
1576         
1577         if (this.html.length) {
1578             cfg.html = this.html;
1579         }
1580         if (this.fa) {
1581             var fasize = '';
1582             if (this.fasize > 1) {
1583                 fasize = ' fa-' + this.fasize + 'x';
1584             }
1585             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1586             
1587             
1588         }
1589         if (this.icon) {
1590             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1591         }
1592         
1593         return cfg;
1594     }
1595    
1596 });
1597
1598  
1599
1600  /*
1601  * - LGPL
1602  *
1603  * page container.
1604  * 
1605  */
1606
1607
1608 /**
1609  * @class Roo.bootstrap.Container
1610  * @extends Roo.bootstrap.Component
1611  * @children Roo.bootstrap.Component
1612  * @parent builder
1613  * Bootstrap Container class
1614  * @cfg {Boolean} jumbotron is it a jumbotron element
1615  * @cfg {String} html content of element
1616  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1617  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1618  * @cfg {String} header content of header (for panel)
1619  * @cfg {String} footer content of footer (for panel)
1620  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1621  * @cfg {String} tag (header|aside|section) type of HTML tag.
1622  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1623  * @cfg {String} fa font awesome icon
1624  * @cfg {String} icon (info-sign|check|...) glyphicon name
1625  * @cfg {Boolean} hidden (true|false) hide the element
1626  * @cfg {Boolean} expandable (true|false) default false
1627  * @cfg {Boolean} expanded (true|false) default true
1628  * @cfg {String} rheader contet on the right of header
1629  * @cfg {Boolean} clickable (true|false) default false
1630
1631  *     
1632  * @constructor
1633  * Create a new Container
1634  * @param {Object} config The config object
1635  */
1636
1637 Roo.bootstrap.Container = function(config){
1638     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1639     
1640     this.addEvents({
1641         // raw events
1642          /**
1643          * @event expand
1644          * After the panel has been expand
1645          * 
1646          * @param {Roo.bootstrap.Container} this
1647          */
1648         "expand" : true,
1649         /**
1650          * @event collapse
1651          * After the panel has been collapsed
1652          * 
1653          * @param {Roo.bootstrap.Container} this
1654          */
1655         "collapse" : true,
1656         /**
1657          * @event click
1658          * When a element is chick
1659          * @param {Roo.bootstrap.Container} this
1660          * @param {Roo.EventObject} e
1661          */
1662         "click" : true
1663     });
1664 };
1665
1666 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1667     
1668     jumbotron : false,
1669     well: '',
1670     panel : '',
1671     header: '',
1672     footer : '',
1673     sticky: '',
1674     tag : false,
1675     alert : false,
1676     fa: false,
1677     icon : false,
1678     expandable : false,
1679     rheader : '',
1680     expanded : true,
1681     clickable: false,
1682   
1683      
1684     getChildContainer : function() {
1685         
1686         if(!this.el){
1687             return false;
1688         }
1689         
1690         if (this.panel.length) {
1691             return this.el.select('.panel-body',true).first();
1692         }
1693         
1694         return this.el;
1695     },
1696     
1697     
1698     getAutoCreate : function(){
1699         
1700         var cfg = {
1701             tag : this.tag || 'div',
1702             html : '',
1703             cls : ''
1704         };
1705         if (this.jumbotron) {
1706             cfg.cls = 'jumbotron';
1707         }
1708         
1709         
1710         
1711         // - this is applied by the parent..
1712         //if (this.cls) {
1713         //    cfg.cls = this.cls + '';
1714         //}
1715         
1716         if (this.sticky.length) {
1717             
1718             var bd = Roo.get(document.body);
1719             if (!bd.hasClass('bootstrap-sticky')) {
1720                 bd.addClass('bootstrap-sticky');
1721                 Roo.select('html',true).setStyle('height', '100%');
1722             }
1723              
1724             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1725         }
1726         
1727         
1728         if (this.well.length) {
1729             switch (this.well) {
1730                 case 'lg':
1731                 case 'sm':
1732                     cfg.cls +=' well well-' +this.well;
1733                     break;
1734                 default:
1735                     cfg.cls +=' well';
1736                     break;
1737             }
1738         }
1739         
1740         if (this.hidden) {
1741             cfg.cls += ' hidden';
1742         }
1743         
1744         
1745         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1746             cfg.cls +=' alert alert-' + this.alert;
1747         }
1748         
1749         var body = cfg;
1750         
1751         if (this.panel.length) {
1752             cfg.cls += ' panel panel-' + this.panel;
1753             cfg.cn = [];
1754             if (this.header.length) {
1755                 
1756                 var h = [];
1757                 
1758                 if(this.expandable){
1759                     
1760                     cfg.cls = cfg.cls + ' expandable';
1761                     
1762                     h.push({
1763                         tag: 'i',
1764                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1765                     });
1766                     
1767                 }
1768                 
1769                 h.push(
1770                     {
1771                         tag: 'span',
1772                         cls : 'panel-title',
1773                         html : (this.expandable ? '&nbsp;' : '') + this.header
1774                     },
1775                     {
1776                         tag: 'span',
1777                         cls: 'panel-header-right',
1778                         html: this.rheader
1779                     }
1780                 );
1781                 
1782                 cfg.cn.push({
1783                     cls : 'panel-heading',
1784                     style : this.expandable ? 'cursor: pointer' : '',
1785                     cn : h
1786                 });
1787                 
1788             }
1789             
1790             body = false;
1791             cfg.cn.push({
1792                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1793                 html : this.html
1794             });
1795             
1796             
1797             if (this.footer.length) {
1798                 cfg.cn.push({
1799                     cls : 'panel-footer',
1800                     html : this.footer
1801                     
1802                 });
1803             }
1804             
1805         }
1806         
1807         if (body) {
1808             body.html = this.html || cfg.html;
1809             // prefix with the icons..
1810             if (this.fa) {
1811                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1812             }
1813             if (this.icon) {
1814                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1815             }
1816             
1817             
1818         }
1819         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1820             cfg.cls =  'container';
1821         }
1822         
1823         return cfg;
1824     },
1825     
1826     initEvents: function() 
1827     {
1828         if(this.expandable){
1829             var headerEl = this.headerEl();
1830         
1831             if(headerEl){
1832                 headerEl.on('click', this.onToggleClick, this);
1833             }
1834         }
1835         
1836         if(this.clickable){
1837             this.el.on('click', this.onClick, this);
1838         }
1839         
1840     },
1841     
1842     onToggleClick : function()
1843     {
1844         var headerEl = this.headerEl();
1845         
1846         if(!headerEl){
1847             return;
1848         }
1849         
1850         if(this.expanded){
1851             this.collapse();
1852             return;
1853         }
1854         
1855         this.expand();
1856     },
1857     
1858     expand : function()
1859     {
1860         if(this.fireEvent('expand', this)) {
1861             
1862             this.expanded = true;
1863             
1864             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1865             
1866             this.el.select('.panel-body',true).first().removeClass('hide');
1867             
1868             var toggleEl = this.toggleEl();
1869
1870             if(!toggleEl){
1871                 return;
1872             }
1873
1874             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1875         }
1876         
1877     },
1878     
1879     collapse : function()
1880     {
1881         if(this.fireEvent('collapse', this)) {
1882             
1883             this.expanded = false;
1884             
1885             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1886             this.el.select('.panel-body',true).first().addClass('hide');
1887         
1888             var toggleEl = this.toggleEl();
1889
1890             if(!toggleEl){
1891                 return;
1892             }
1893
1894             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1895         }
1896     },
1897     
1898     toggleEl : function()
1899     {
1900         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1901             return;
1902         }
1903         
1904         return this.el.select('.panel-heading .fa',true).first();
1905     },
1906     
1907     headerEl : function()
1908     {
1909         if(!this.el || !this.panel.length || !this.header.length){
1910             return;
1911         }
1912         
1913         return this.el.select('.panel-heading',true).first()
1914     },
1915     
1916     bodyEl : function()
1917     {
1918         if(!this.el || !this.panel.length){
1919             return;
1920         }
1921         
1922         return this.el.select('.panel-body',true).first()
1923     },
1924     
1925     titleEl : function()
1926     {
1927         if(!this.el || !this.panel.length || !this.header.length){
1928             return;
1929         }
1930         
1931         return this.el.select('.panel-title',true).first();
1932     },
1933     
1934     setTitle : function(v)
1935     {
1936         var titleEl = this.titleEl();
1937         
1938         if(!titleEl){
1939             return;
1940         }
1941         
1942         titleEl.dom.innerHTML = v;
1943     },
1944     
1945     getTitle : function()
1946     {
1947         
1948         var titleEl = this.titleEl();
1949         
1950         if(!titleEl){
1951             return '';
1952         }
1953         
1954         return titleEl.dom.innerHTML;
1955     },
1956     
1957     setRightTitle : function(v)
1958     {
1959         var t = this.el.select('.panel-header-right',true).first();
1960         
1961         if(!t){
1962             return;
1963         }
1964         
1965         t.dom.innerHTML = v;
1966     },
1967     
1968     onClick : function(e)
1969     {
1970         e.preventDefault();
1971         
1972         this.fireEvent('click', this, e);
1973     }
1974 });
1975
1976  /**
1977  * @class Roo.bootstrap.Card
1978  * @extends Roo.bootstrap.Component
1979  * @children Roo.bootstrap.Component
1980  * @licence LGPL
1981  * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1982  *
1983  *
1984  * possible... may not be implemented..
1985  * @cfg {String} header_image  src url of image.
1986  * @cfg {String|Object} header
1987  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1988  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1989  * 
1990  * @cfg {String} title
1991  * @cfg {String} subtitle
1992  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1993  * @cfg {String} footer
1994  
1995  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1996  * 
1997  * @cfg {String} margin (0|1|2|3|4|5|auto)
1998  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1999  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
2000  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
2001  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2002  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2003  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2004  *
2005  * @cfg {String} padding (0|1|2|3|4|5)
2006  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2007  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2008  * @cfg {String} padding_left (0|1|2|3|4|5)
2009  * @cfg {String} padding_right (0|1|2|3|4|5)
2010  * @cfg {String} padding_x (0|1|2|3|4|5)
2011  * @cfg {String} padding_y (0|1|2|3|4|5)
2012  *
2013  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2016  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2017  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2018  
2019  * @config {Boolean} dragable  if this card can be dragged.
2020  * @config {String} drag_group  group for drag
2021  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2022  * @config {String} drop_group  group for drag
2023  * 
2024  * @config {Boolean} collapsable can the body be collapsed.
2025  * @config {Boolean} collapsed is the body collapsed when rendered...
2026  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2027  * @config {Boolean} rotated is the body rotated when rendered...
2028  * 
2029  * @constructor
2030  * Create a new Container
2031  * @param {Object} config The config object
2032  */
2033
2034 Roo.bootstrap.Card = function(config){
2035     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2036     
2037     this.addEvents({
2038          // raw events
2039         /**
2040          * @event drop
2041          * When a element a card is dropped
2042          * @param {Roo.bootstrap.Card} this
2043          *
2044          * 
2045          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2046          * @param {String} position 'above' or 'below'
2047          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2048         
2049          */
2050         'drop' : true,
2051          /**
2052          * @event rotate
2053          * When a element a card is rotate
2054          * @param {Roo.bootstrap.Card} this
2055          * @param {Roo.Element} n the node being dropped?
2056          * @param {Boolean} rotate status
2057          */
2058         'rotate' : true,
2059         /**
2060          * @event cardover
2061          * When a card element is dragged over ready to drop (return false to block dropable)
2062          * @param {Roo.bootstrap.Card} this
2063          * @param {Object} data from dragdrop 
2064          */
2065          'cardover' : true
2066          
2067     });
2068 };
2069
2070
2071 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2072     
2073     
2074     weight : '',
2075     
2076     margin: '', /// may be better in component?
2077     margin_top: '', 
2078     margin_bottom: '', 
2079     margin_left: '',
2080     margin_right: '',
2081     margin_x: '',
2082     margin_y: '',
2083     
2084     padding : '',
2085     padding_top: '', 
2086     padding_bottom: '', 
2087     padding_left: '',
2088     padding_right: '',
2089     padding_x: '',
2090     padding_y: '',
2091     
2092     display: '', 
2093     display_xs: '', 
2094     display_sm: '', 
2095     display_lg: '',
2096     display_xl: '',
2097  
2098     header_image  : '',
2099     header : '',
2100     header_size : 0,
2101     title : '',
2102     subtitle : '',
2103     html : '',
2104     footer: '',
2105
2106     collapsable : false,
2107     collapsed : false,
2108     rotateable : false,
2109     rotated : false,
2110     
2111     dragable : false,
2112     drag_group : false,
2113     dropable : false,
2114     drop_group : false,
2115     childContainer : false,
2116     dropEl : false, /// the dom placeholde element that indicates drop location.
2117     containerEl: false, // body container
2118     bodyEl: false, // card-body
2119     headerContainerEl : false, //
2120     headerEl : false,
2121     header_imageEl : false,
2122     
2123     
2124     layoutCls : function()
2125     {
2126         var cls = '';
2127         var t = this;
2128         Roo.log(this.margin_bottom.length);
2129         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2130             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2131             
2132             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2133                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2134             }
2135             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2136                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2137             }
2138         });
2139         
2140         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2141             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2142                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2143             }
2144         });
2145         
2146         // more generic support?
2147         if (this.hidden) {
2148             cls += ' d-none';
2149         }
2150         
2151         return cls;
2152     },
2153  
2154        // Roo.log("Call onRender: " + this.xtype);
2155         /*  We are looking at something like this.
2156 <div class="card">
2157     <img src="..." class="card-img-top" alt="...">
2158     <div class="card-body">
2159         <h5 class="card-title">Card title</h5>
2160          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2161
2162         >> this bit is really the body...
2163         <div> << we will ad dthis in hopefully it will not break shit.
2164         
2165         ** card text does not actually have any styling...
2166         
2167             <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>
2168         
2169         </div> <<
2170           <a href="#" class="card-link">Card link</a>
2171           
2172     </div>
2173     <div class="card-footer">
2174         <small class="text-muted">Last updated 3 mins ago</small>
2175     </div>
2176 </div>
2177          */
2178     getAutoCreate : function(){
2179         
2180         var cfg = {
2181             tag : 'div',
2182             cls : 'card',
2183             cn : [ ]
2184         };
2185         
2186         if (this.weight.length && this.weight != 'light') {
2187             cfg.cls += ' text-white';
2188         } else {
2189             cfg.cls += ' text-dark'; // need as it's nested..
2190         }
2191         if (this.weight.length) {
2192             cfg.cls += ' bg-' + this.weight;
2193         }
2194         
2195         cfg.cls += ' ' + this.layoutCls(); 
2196         
2197         var hdr = false;
2198         var hdr_ctr = false;
2199         if (this.header.length) {
2200             hdr = {
2201                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2202                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2203                 cn : []
2204             };
2205             cfg.cn.push(hdr);
2206             hdr_ctr = hdr;
2207         } else {
2208             hdr = {
2209                 tag : 'div',
2210                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2211                 cn : []
2212             };
2213             cfg.cn.push(hdr);
2214             hdr_ctr = hdr;
2215         }
2216         if (this.collapsable) {
2217             hdr_ctr = {
2218             tag : 'a',
2219             cls : 'd-block user-select-none',
2220             cn: [
2221                     {
2222                         tag: 'i',
2223                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2224                     }
2225                    
2226                 ]
2227             };
2228             hdr.cn.push(hdr_ctr);
2229         }
2230         
2231         hdr_ctr.cn.push(        {
2232             tag: 'span',
2233             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2234             html : this.header
2235         });
2236         
2237         
2238         if (this.header_image.length) {
2239             cfg.cn.push({
2240                 tag : 'img',
2241                 cls : 'card-img-top',
2242                 src: this.header_image // escape?
2243             });
2244         } else {
2245             cfg.cn.push({
2246                     tag : 'div',
2247                     cls : 'card-img-top d-none' 
2248                 });
2249         }
2250             
2251         var body = {
2252             tag : 'div',
2253             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2254             cn : []
2255         };
2256         var obody = body;
2257         if (this.collapsable || this.rotateable) {
2258             obody = {
2259                 tag: 'div',
2260                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2261                 cn : [  body ]
2262             };
2263         }
2264         
2265         cfg.cn.push(obody);
2266         
2267         if (this.title.length) {
2268             body.cn.push({
2269                 tag : 'div',
2270                 cls : 'card-title',
2271                 src: this.title // escape?
2272             });
2273         }  
2274         
2275         if (this.subtitle.length) {
2276             body.cn.push({
2277                 tag : 'div',
2278                 cls : 'card-title',
2279                 src: this.subtitle // escape?
2280             });
2281         }
2282         
2283         body.cn.push({
2284             tag : 'div',
2285             cls : 'roo-card-body-ctr'
2286         });
2287         
2288         if (this.html.length) {
2289             body.cn.push({
2290                 tag: 'div',
2291                 html : this.html
2292             });
2293         }
2294         // fixme ? handle objects?
2295         
2296         if (this.footer.length) {
2297            
2298             cfg.cn.push({
2299                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2300                 html : this.footer
2301             });
2302             
2303         } else {
2304             cfg.cn.push({cls : 'card-footer d-none'});
2305         }
2306         
2307         // footer...
2308         
2309         return cfg;
2310     },
2311     
2312     
2313     getCardHeader : function()
2314     {
2315         var  ret = this.el.select('.card-header',true).first();
2316         if (ret.hasClass('d-none')) {
2317             ret.removeClass('d-none');
2318         }
2319         
2320         return ret;
2321     },
2322     getCardFooter : function()
2323     {
2324         var  ret = this.el.select('.card-footer',true).first();
2325         if (ret.hasClass('d-none')) {
2326             ret.removeClass('d-none');
2327         }
2328         
2329         return ret;
2330     },
2331     getCardImageTop : function()
2332     {
2333         var  ret = this.header_imageEl;
2334         if (ret.hasClass('d-none')) {
2335             ret.removeClass('d-none');
2336         }
2337             
2338         return ret;
2339     },
2340     
2341     getChildContainer : function()
2342     {
2343         
2344         if(!this.el){
2345             return false;
2346         }
2347         return this.el.select('.roo-card-body-ctr',true).first();    
2348     },
2349     
2350     initEvents: function() 
2351     {
2352         this.bodyEl = this.el.select('.card-body',true).first(); 
2353         this.containerEl = this.getChildContainer();
2354         if(this.dragable){
2355             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2356                     containerScroll: true,
2357                     ddGroup: this.drag_group || 'default_card_drag_group'
2358             });
2359             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2360         }
2361         if (this.dropable) {
2362             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2363                 containerScroll: true,
2364                 ddGroup: this.drop_group || 'default_card_drag_group'
2365             });
2366             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2367             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2368             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2369             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2370             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2371         }
2372         
2373         if (this.collapsable) {
2374             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2375         }
2376         if (this.rotateable) {
2377             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2378         }
2379         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2380          
2381         this.footerEl = this.el.select('.card-footer',true).first();
2382         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2383         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2384         this.headerEl = this.el.select('.card-header',true).first();
2385         
2386         if (this.rotated) {
2387             this.el.addClass('roo-card-rotated');
2388             this.fireEvent('rotate', this, true);
2389         }
2390         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2391         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2392         
2393     },
2394     getDragData : function(e)
2395     {
2396         var target = this.getEl();
2397         if (target) {
2398             //this.handleSelection(e);
2399             
2400             var dragData = {
2401                 source: this,
2402                 copy: false,
2403                 nodes: this.getEl(),
2404                 records: []
2405             };
2406             
2407             
2408             dragData.ddel = target.dom ;    // the div element
2409             Roo.log(target.getWidth( ));
2410             dragData.ddel.style.width = target.getWidth() + 'px';
2411             
2412             return dragData;
2413         }
2414         return false;
2415     },
2416     /**
2417     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2418     *    whole Element becomes the target, and this causes the drop gesture to append.
2419     *
2420     *    Returns an object:
2421     *     {
2422            
2423            position : 'below' or 'above'
2424            card  : relateive to card OBJECT (or true for no cards listed)
2425            items_n : relative to nth item in list
2426            card_n : relative to  nth card in list
2427     }
2428     *
2429     *    
2430     */
2431     getTargetFromEvent : function(e, dragged_card_el)
2432     {
2433         var target = e.getTarget();
2434         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2435             target = target.parentNode;
2436         }
2437         
2438         var ret = {
2439             position: '',
2440             cards : [],
2441             card_n : -1,
2442             items_n : -1,
2443             card : false 
2444         };
2445         
2446         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2447         // see if target is one of the 'cards'...
2448         
2449         
2450         //Roo.log(this.items.length);
2451         var pos = false;
2452         
2453         var last_card_n = 0;
2454         var cards_len  = 0;
2455         for (var i = 0;i< this.items.length;i++) {
2456             
2457             if (!this.items[i].el.hasClass('card')) {
2458                  continue;
2459             }
2460             pos = this.getDropPoint(e, this.items[i].el.dom);
2461             
2462             cards_len = ret.cards.length;
2463             //Roo.log(this.items[i].el.dom.id);
2464             ret.cards.push(this.items[i]);
2465             last_card_n  = i;
2466             if (ret.card_n < 0 && pos == 'above') {
2467                 ret.position = cards_len > 0 ? 'below' : pos;
2468                 ret.items_n = i > 0 ? i - 1 : 0;
2469                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2470                 ret.card = ret.cards[ret.card_n];
2471             }
2472         }
2473         if (!ret.cards.length) {
2474             ret.card = true;
2475             ret.position = 'below';
2476             ret.items_n;
2477             return ret;
2478         }
2479         // could not find a card.. stick it at the end..
2480         if (ret.card_n < 0) {
2481             ret.card_n = last_card_n;
2482             ret.card = ret.cards[last_card_n];
2483             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2484             ret.position = 'below';
2485         }
2486         
2487         if (this.items[ret.items_n].el == dragged_card_el) {
2488             return false;
2489         }
2490         
2491         if (ret.position == 'below') {
2492             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2493             
2494             if (card_after  && card_after.el == dragged_card_el) {
2495                 return false;
2496             }
2497             return ret;
2498         }
2499         
2500         // its's after ..
2501         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2502         
2503         if (card_before  && card_before.el == dragged_card_el) {
2504             return false;
2505         }
2506         
2507         return ret;
2508     },
2509     
2510     onNodeEnter : function(n, dd, e, data){
2511         return false;
2512     },
2513     onNodeOver : function(n, dd, e, data)
2514     {
2515        
2516         var target_info = this.getTargetFromEvent(e,data.source.el);
2517         if (target_info === false) {
2518             this.dropPlaceHolder('hide');
2519             return false;
2520         }
2521         Roo.log(['getTargetFromEvent', target_info ]);
2522         
2523         
2524         if (this.fireEvent('cardover', this, [ data ]) === false) {
2525             return false;
2526         }
2527         
2528         this.dropPlaceHolder('show', target_info,data);
2529         
2530         return false; 
2531     },
2532     onNodeOut : function(n, dd, e, data){
2533         this.dropPlaceHolder('hide');
2534      
2535     },
2536     onNodeDrop : function(n, dd, e, data)
2537     {
2538         
2539         // call drop - return false if
2540         
2541         // this could actually fail - if the Network drops..
2542         // we will ignore this at present..- client should probably reload
2543         // the whole set of cards if stuff like that fails.
2544         
2545         
2546         var info = this.getTargetFromEvent(e,data.source.el);
2547         if (info === false) {
2548             return false;
2549         }
2550         this.dropPlaceHolder('hide');
2551   
2552           
2553     
2554         this.acceptCard(data.source, info.position, info.card, info.items_n);
2555         return true;
2556          
2557     },
2558     firstChildCard : function()
2559     {
2560         for (var i = 0;i< this.items.length;i++) {
2561             
2562             if (!this.items[i].el.hasClass('card')) {
2563                  continue;
2564             }
2565             return this.items[i];
2566         }
2567         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2568     },
2569     /**
2570      * accept card
2571      *
2572      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2573      */
2574     acceptCard : function(move_card,  position, next_to_card )
2575     {
2576         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2577             return false;
2578         }
2579         
2580         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2581         
2582         move_card.parent().removeCard(move_card);
2583         
2584         
2585         var dom = move_card.el.dom;
2586         dom.style.width = ''; // clear with - which is set by drag.
2587         
2588         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2589             var cardel = next_to_card.el.dom;
2590             
2591             if (position == 'above' ) {
2592                 cardel.parentNode.insertBefore(dom, cardel);
2593             } else if (cardel.nextSibling) {
2594                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2595             } else {
2596                 cardel.parentNode.append(dom);
2597             }
2598         } else {
2599             // card container???
2600             this.containerEl.dom.append(dom);
2601         }
2602         
2603         //FIXME HANDLE card = true 
2604         
2605         // add this to the correct place in items.
2606         
2607         // remove Card from items.
2608         
2609        
2610         if (this.items.length) {
2611             var nitems = [];
2612             //Roo.log([info.items_n, info.position, this.items.length]);
2613             for (var i =0; i < this.items.length; i++) {
2614                 if (i == to_items_n && position == 'above') {
2615                     nitems.push(move_card);
2616                 }
2617                 nitems.push(this.items[i]);
2618                 if (i == to_items_n && position == 'below') {
2619                     nitems.push(move_card);
2620                 }
2621             }
2622             this.items = nitems;
2623             Roo.log(this.items);
2624         } else {
2625             this.items.push(move_card);
2626         }
2627         
2628         move_card.parentId = this.id;
2629         
2630         return true;
2631         
2632         
2633     },
2634     removeCard : function(c)
2635     {
2636         this.items = this.items.filter(function(e) { return e != c });
2637  
2638         var dom = c.el.dom;
2639         dom.parentNode.removeChild(dom);
2640         dom.style.width = ''; // clear with - which is set by drag.
2641         c.parentId = false;
2642         
2643     },
2644     
2645     /**    Decide whether to drop above or below a View node. */
2646     getDropPoint : function(e, n, dd)
2647     {
2648         if (dd) {
2649              return false;
2650         }
2651         if (n == this.containerEl.dom) {
2652             return "above";
2653         }
2654         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2655         var c = t + (b - t) / 2;
2656         var y = Roo.lib.Event.getPageY(e);
2657         if(y <= c) {
2658             return "above";
2659         }else{
2660             return "below";
2661         }
2662     },
2663     onToggleCollapse : function(e)
2664         {
2665         if (this.collapsed) {
2666             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2667             this.collapsableEl.addClass('show');
2668             this.collapsed = false;
2669             return;
2670         }
2671         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2672         this.collapsableEl.removeClass('show');
2673         this.collapsed = true;
2674         
2675     
2676     },
2677     
2678     onToggleRotate : function(e)
2679     {
2680         this.collapsableEl.removeClass('show');
2681         this.footerEl.removeClass('d-none');
2682         this.el.removeClass('roo-card-rotated');
2683         this.el.removeClass('d-none');
2684         if (this.rotated) {
2685             
2686             this.collapsableEl.addClass('show');
2687             this.rotated = false;
2688             this.fireEvent('rotate', this, this.rotated);
2689             return;
2690         }
2691         this.el.addClass('roo-card-rotated');
2692         this.footerEl.addClass('d-none');
2693         this.el.select('.roo-collapsable').removeClass('show');
2694         
2695         this.rotated = true;
2696         this.fireEvent('rotate', this, this.rotated);
2697     
2698     },
2699     
2700     dropPlaceHolder: function (action, info, data)
2701     {
2702         if (this.dropEl === false) {
2703             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2704             cls : 'd-none'
2705             },true);
2706         }
2707         this.dropEl.removeClass(['d-none', 'd-block']);        
2708         if (action == 'hide') {
2709             
2710             this.dropEl.addClass('d-none');
2711             return;
2712         }
2713         // FIXME - info.card == true!!!
2714         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2715         
2716         if (info.card !== true) {
2717             var cardel = info.card.el.dom;
2718             
2719             if (info.position == 'above') {
2720                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2721             } else if (cardel.nextSibling) {
2722                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2723             } else {
2724                 cardel.parentNode.append(this.dropEl.dom);
2725             }
2726         } else {
2727             // card container???
2728             this.containerEl.dom.append(this.dropEl.dom);
2729         }
2730         
2731         this.dropEl.addClass('d-block roo-card-dropzone');
2732         
2733         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2734         
2735         
2736     
2737     
2738     
2739     },
2740     setHeaderText: function(html)
2741     {
2742         this.header = html;
2743         if (this.headerContainerEl) {
2744             this.headerContainerEl.dom.innerHTML = html;
2745         }
2746     },
2747     onHeaderImageLoad : function(ev, he)
2748     {
2749         if (!this.header_image_fit_square) {
2750             return;
2751         }
2752         
2753         var hw = he.naturalHeight / he.naturalWidth;
2754         // wide image = < 0
2755         // tall image = > 1
2756         //var w = he.dom.naturalWidth;
2757         var ww = he.width;
2758         he.style.left =  0;
2759         he.style.position =  'relative';
2760         if (hw > 1) {
2761             var nw = (ww * (1/hw));
2762             Roo.get(he).setSize( ww * (1/hw),  ww);
2763             he.style.left =  ((ww - nw)/ 2) + 'px';
2764             he.style.position =  'relative';
2765         }
2766
2767     }
2768
2769     
2770 });
2771
2772 /*
2773  * - LGPL
2774  *
2775  * Card header - holder for the card header elements.
2776  * 
2777  */
2778
2779 /**
2780  * @class Roo.bootstrap.CardHeader
2781  * @extends Roo.bootstrap.Element
2782  * @parent Roo.bootstrap.Card
2783  * @children Roo.bootstrap.Component
2784  * Bootstrap CardHeader class
2785  * @constructor
2786  * Create a new Card Header - that you can embed children into
2787  * @param {Object} config The config object
2788  */
2789
2790 Roo.bootstrap.CardHeader = function(config){
2791     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2792 };
2793
2794 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2795     
2796     
2797     container_method : 'getCardHeader' 
2798     
2799      
2800     
2801     
2802    
2803 });
2804
2805  
2806
2807  /*
2808  * - LGPL
2809  *
2810  * Card footer - holder for the card footer elements.
2811  * 
2812  */
2813
2814 /**
2815  * @class Roo.bootstrap.CardFooter
2816  * @extends Roo.bootstrap.Element
2817  * @parent Roo.bootstrap.Card
2818  * @children Roo.bootstrap.Component
2819  * Bootstrap CardFooter class
2820  * 
2821  * @constructor
2822  * Create a new Card Footer - that you can embed children into
2823  * @param {Object} config The config object
2824  */
2825
2826 Roo.bootstrap.CardFooter = function(config){
2827     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2828 };
2829
2830 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2831     
2832     
2833     container_method : 'getCardFooter' 
2834     
2835      
2836     
2837     
2838    
2839 });
2840
2841  
2842
2843  /*
2844  * - LGPL
2845  *
2846  * Card header - holder for the card header elements.
2847  * 
2848  */
2849
2850 /**
2851  * @class Roo.bootstrap.CardImageTop
2852  * @extends Roo.bootstrap.Element
2853  * @parent Roo.bootstrap.Card
2854  * @children Roo.bootstrap.Component
2855  * Bootstrap CardImageTop class
2856  * 
2857  * @constructor
2858  * Create a new Card Image Top container
2859  * @param {Object} config The config object
2860  */
2861
2862 Roo.bootstrap.CardImageTop = function(config){
2863     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2864 };
2865
2866 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2867     
2868    
2869     container_method : 'getCardImageTop' 
2870     
2871      
2872     
2873    
2874 });
2875
2876  
2877
2878  
2879 /*
2880 * Licence: LGPL
2881 */
2882
2883 /**
2884  * @class Roo.bootstrap.ButtonUploader
2885  * @extends Roo.bootstrap.Button
2886  * Bootstrap Button Uploader class - it's a button which when you add files to it
2887  *
2888  * 
2889  * @cfg {Number} errorTimeout default 3000
2890  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2891  * @cfg {Array}  html The button text.
2892  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2893  *
2894  * @constructor
2895  * Create a new CardUploader
2896  * @param {Object} config The config object
2897  */
2898
2899 Roo.bootstrap.ButtonUploader = function(config){
2900     
2901  
2902     
2903     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2904     
2905      
2906      this.addEvents({
2907          // raw events
2908         /**
2909          * @event beforeselect
2910          * When button is pressed, before show upload files dialog is shown
2911          * @param {Roo.bootstrap.UploaderButton} this
2912          *
2913          */
2914         'beforeselect' : true,
2915          /**
2916          * @event fired when files have been selected, 
2917          * When a the download link is clicked
2918          * @param {Roo.bootstrap.UploaderButton} this
2919          * @param {Array} Array of files that have been uploaded
2920          */
2921         'uploaded' : true
2922         
2923     });
2924 };
2925  
2926 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2927     
2928      
2929     errorTimeout : 3000,
2930      
2931     images : false,
2932    
2933     fileCollection : false,
2934     allowBlank : true,
2935     
2936     multiple : true,
2937     
2938     getAutoCreate : function()
2939     {
2940        
2941         
2942         return  {
2943             cls :'div' ,
2944             cn : [
2945                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this) 
2946             ]
2947         };
2948            
2949          
2950     },
2951      
2952    
2953     initEvents : function()
2954     {
2955         
2956         Roo.bootstrap.Button.prototype.initEvents.call(this);
2957         
2958         
2959         
2960         
2961         
2962         this.urlAPI = (window.createObjectURL && window) || 
2963                                 (window.URL && URL.revokeObjectURL && URL) || 
2964                                 (window.webkitURL && webkitURL);
2965                         
2966         var im = {
2967             tag: 'input',
2968             type : 'file',
2969             cls : 'd-none  roo-card-upload-selector' 
2970           
2971         };
2972         if (this.multiple) {
2973             im.multiple = 'multiple';
2974         }
2975         this.selectorEl = Roo.get(document.body).createChild(im); // so it does not capture click event for navitem.
2976        
2977         //this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2978         
2979         this.selectorEl.on('change', this.onFileSelected, this);
2980          
2981          
2982        
2983     },
2984     
2985    
2986     onClick : function(e)
2987     {
2988         e.preventDefault();
2989         
2990         if ( this.fireEvent('beforeselect', this) === false) {
2991             return;
2992         }
2993          
2994         this.selectorEl.dom.click();
2995          
2996     },
2997     
2998     onFileSelected : function(e)
2999     {
3000         e.preventDefault();
3001         
3002         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3003             return;
3004         }
3005         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3006         this.selectorEl.dom.value  = '';// hopefully reset..
3007         
3008         this.fireEvent('uploaded', this,  files );
3009         
3010     },
3011     
3012        
3013    
3014     
3015     /**
3016      * addCard - add an Attachment to the uploader
3017      * @param data - the data about the image to upload
3018      *
3019      * {
3020           id : 123
3021           title : "Title of file",
3022           is_uploaded : false,
3023           src : "http://.....",
3024           srcfile : { the File upload object },
3025           mimetype : file.type,
3026           preview : false,
3027           is_deleted : 0
3028           .. any other data...
3029         }
3030      *
3031      * 
3032     */
3033      
3034     reset: function()
3035     {
3036          
3037          this.selectorEl
3038     } 
3039     
3040     
3041     
3042     
3043 });
3044  /*
3045  * - LGPL
3046  *
3047  * image
3048  * 
3049  */
3050
3051
3052 /**
3053  * @class Roo.bootstrap.Img
3054  * @extends Roo.bootstrap.Component
3055  * Bootstrap Img class
3056  * @cfg {Boolean} imgResponsive false | true
3057  * @cfg {String} border rounded | circle | thumbnail
3058  * @cfg {String} src image source
3059  * @cfg {String} alt image alternative text
3060  * @cfg {String} href a tag href
3061  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3062  * @cfg {String} xsUrl xs image source
3063  * @cfg {String} smUrl sm image source
3064  * @cfg {String} mdUrl md image source
3065  * @cfg {String} lgUrl lg image source
3066  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3067  * 
3068  * @constructor
3069  * Create a new Input
3070  * @param {Object} config The config object
3071  */
3072
3073 Roo.bootstrap.Img = function(config){
3074     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3075     
3076     this.addEvents({
3077         // img events
3078         /**
3079          * @event click
3080          * The img click event for the img.
3081          * @param {Roo.EventObject} e
3082          */
3083         "click" : true,
3084         /**
3085          * @event load
3086          * The when any image loads
3087          * @param {Roo.EventObject} e
3088          */
3089         "load" : true
3090     });
3091 };
3092
3093 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3094     
3095     imgResponsive: true,
3096     border: '',
3097     src: 'about:blank',
3098     href: false,
3099     target: false,
3100     xsUrl: '',
3101     smUrl: '',
3102     mdUrl: '',
3103     lgUrl: '',
3104     backgroundContain : false,
3105
3106     getAutoCreate : function()
3107     {   
3108         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3109             return this.createSingleImg();
3110         }
3111         
3112         var cfg = {
3113             tag: 'div',
3114             cls: 'roo-image-responsive-group',
3115             cn: []
3116         };
3117         var _this = this;
3118         
3119         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3120             
3121             if(!_this[size + 'Url']){
3122                 return;
3123             }
3124             
3125             var img = {
3126                 tag: 'img',
3127                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3128                 html: _this.html || cfg.html,
3129                 src: _this[size + 'Url']
3130             };
3131             
3132             img.cls += ' roo-image-responsive-' + size;
3133             
3134             var s = ['xs', 'sm', 'md', 'lg'];
3135             
3136             s.splice(s.indexOf(size), 1);
3137             
3138             Roo.each(s, function(ss){
3139                 img.cls += ' hidden-' + ss;
3140             });
3141             
3142             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3143                 cfg.cls += ' img-' + _this.border;
3144             }
3145             
3146             if(_this.alt){
3147                 cfg.alt = _this.alt;
3148             }
3149             
3150             if(_this.href){
3151                 var a = {
3152                     tag: 'a',
3153                     href: _this.href,
3154                     cn: [
3155                         img
3156                     ]
3157                 };
3158
3159                 if(this.target){
3160                     a.target = _this.target;
3161                 }
3162             }
3163             
3164             cfg.cn.push((_this.href) ? a : img);
3165             
3166         });
3167         
3168         return cfg;
3169     },
3170     
3171     createSingleImg : function()
3172     {
3173         var cfg = {
3174             tag: 'img',
3175             cls: (this.imgResponsive) ? 'img-responsive' : '',
3176             html : null,
3177             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3178         };
3179         
3180         if (this.backgroundContain) {
3181             cfg.cls += ' background-contain';
3182         }
3183         
3184         cfg.html = this.html || cfg.html;
3185         
3186         if (this.backgroundContain) {
3187             cfg.style="background-image: url(" + this.src + ')';
3188         } else {
3189             cfg.src = this.src || cfg.src;
3190         }
3191         
3192         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3193             cfg.cls += ' img-' + this.border;
3194         }
3195         
3196         if(this.alt){
3197             cfg.alt = this.alt;
3198         }
3199         
3200         if(this.href){
3201             var a = {
3202                 tag: 'a',
3203                 href: this.href,
3204                 cn: [
3205                     cfg
3206                 ]
3207             };
3208             
3209             if(this.target){
3210                 a.target = this.target;
3211             }
3212             
3213         }
3214         
3215         return (this.href) ? a : cfg;
3216     },
3217     
3218     initEvents: function() 
3219     {
3220         if(!this.href){
3221             this.el.on('click', this.onClick, this);
3222         }
3223         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3224             this.el.on('load', this.onImageLoad, this);
3225         } else {
3226             // not sure if this works.. not tested
3227             this.el.select('img', true).on('load', this.onImageLoad, this);
3228         }
3229         
3230     },
3231     
3232     onClick : function(e)
3233     {
3234         Roo.log('img onclick');
3235         this.fireEvent('click', this, e);
3236     },
3237     onImageLoad: function(e)
3238     {
3239         Roo.log('img load');
3240         this.fireEvent('load', this, e);
3241     },
3242     
3243     /**
3244      * Sets the url of the image - used to update it
3245      * @param {String} url the url of the image
3246      */
3247     
3248     setSrc : function(url)
3249     {
3250         this.src =  url;
3251         
3252         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3253             if (this.backgroundContain) {
3254                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3255             } else {
3256                 this.el.dom.src =  url;
3257             }
3258             return;
3259         }
3260         
3261         this.el.select('img', true).first().dom.src =  url;
3262     }
3263     
3264     
3265    
3266 });
3267
3268  /*
3269  * - LGPL
3270  *
3271  * image
3272  * 
3273  */
3274
3275
3276 /**
3277  * @class Roo.bootstrap.Link
3278  * @extends Roo.bootstrap.Component
3279  * @children Roo.bootstrap.Component
3280  * Bootstrap Link Class (eg. '<a href>')
3281  
3282  * @cfg {String} alt image alternative text
3283  * @cfg {String} href a tag href
3284  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3285  * @cfg {String} html the content of the link.
3286  * @cfg {String} anchor name for the anchor link
3287  * @cfg {String} fa - favicon
3288
3289  * @cfg {Boolean} preventDefault (true | false) default false
3290
3291  * 
3292  * @constructor
3293  * Create a new Input
3294  * @param {Object} config The config object
3295  */
3296
3297 Roo.bootstrap.Link = function(config){
3298     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3299     
3300     this.addEvents({
3301         // img events
3302         /**
3303          * @event click
3304          * The img click event for the img.
3305          * @param {Roo.EventObject} e
3306          */
3307         "click" : true
3308     });
3309 };
3310
3311 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3312     
3313     href: false,
3314     target: false,
3315     preventDefault: false,
3316     anchor : false,
3317     alt : false,
3318     fa: false,
3319
3320
3321     getAutoCreate : function()
3322     {
3323         var html = this.html || '';
3324         
3325         if (this.fa !== false) {
3326             html = '<i class="fa fa-' + this.fa + '"></i>';
3327         }
3328         var cfg = {
3329             tag: 'a'
3330         };
3331         // anchor's do not require html/href...
3332         if (this.anchor === false) {
3333             cfg.html = html;
3334             cfg.href = this.href || '#';
3335         } else {
3336             cfg.name = this.anchor;
3337             if (this.html !== false || this.fa !== false) {
3338                 cfg.html = html;
3339             }
3340             if (this.href !== false) {
3341                 cfg.href = this.href;
3342             }
3343         }
3344         
3345         if(this.alt !== false){
3346             cfg.alt = this.alt;
3347         }
3348         
3349         
3350         if(this.target !== false) {
3351             cfg.target = this.target;
3352         }
3353         
3354         return cfg;
3355     },
3356     
3357     initEvents: function() {
3358         
3359         if(!this.href || this.preventDefault){
3360             this.el.on('click', this.onClick, this);
3361         }
3362     },
3363     
3364     onClick : function(e)
3365     {
3366         if(this.preventDefault){
3367             e.preventDefault();
3368         }
3369         //Roo.log('img onclick');
3370         this.fireEvent('click', this, e);
3371     }
3372    
3373 });
3374
3375  /*
3376  * - LGPL
3377  *
3378  * header
3379  * 
3380  */
3381
3382 /**
3383  * @class Roo.bootstrap.Header
3384  * @extends Roo.bootstrap.Component
3385  * @children Roo.bootstrap.Component
3386  * Bootstrap Header class
3387  *
3388  * 
3389  * @cfg {String} html content of header
3390  * @cfg {Number} level (1|2|3|4|5|6) default 1
3391  * 
3392  * @constructor
3393  * Create a new Header
3394  * @param {Object} config The config object
3395  */
3396
3397
3398 Roo.bootstrap.Header  = function(config){
3399     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3400 };
3401
3402 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3403     
3404     //href : false,
3405     html : false,
3406     level : 1,
3407     
3408     
3409     
3410     getAutoCreate : function(){
3411         
3412         
3413         
3414         var cfg = {
3415             tag: 'h' + (1 *this.level),
3416             html: this.html || ''
3417         } ;
3418         
3419         return cfg;
3420     }
3421    
3422 });
3423
3424  
3425
3426  /**
3427  * @class Roo.bootstrap.MenuMgr
3428  * @licence LGPL
3429  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3430  * @static
3431  */
3432 Roo.bootstrap.menu.Manager = function(){
3433    var menus, active, groups = {}, attached = false, lastShow = new Date();
3434
3435    // private - called when first menu is created
3436    function init(){
3437        menus = {};
3438        active = new Roo.util.MixedCollection();
3439        Roo.get(document).addKeyListener(27, function(){
3440            if(active.length > 0){
3441                hideAll();
3442            }
3443        });
3444    }
3445
3446    // private
3447    function hideAll(){
3448        if(active && active.length > 0){
3449            var c = active.clone();
3450            c.each(function(m){
3451                m.hide();
3452            });
3453        }
3454    }
3455
3456    // private
3457    function onHide(m){
3458        active.remove(m);
3459        if(active.length < 1){
3460            Roo.get(document).un("mouseup", onMouseDown);
3461             
3462            attached = false;
3463        }
3464    }
3465
3466    // private
3467    function onShow(m){
3468        var last = active.last();
3469        lastShow = new Date();
3470        active.add(m);
3471        if(!attached){
3472           Roo.get(document).on("mouseup", onMouseDown);
3473            
3474            attached = true;
3475        }
3476        if(m.parentMenu){
3477           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3478           m.parentMenu.activeChild = m;
3479        }else if(last && last.isVisible()){
3480           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3481        }
3482    }
3483
3484    // private
3485    function onBeforeHide(m){
3486        if(m.activeChild){
3487            m.activeChild.hide();
3488        }
3489        if(m.autoHideTimer){
3490            clearTimeout(m.autoHideTimer);
3491            delete m.autoHideTimer;
3492        }
3493    }
3494
3495    // private
3496    function onBeforeShow(m){
3497        var pm = m.parentMenu;
3498        if(!pm && !m.allowOtherMenus){
3499            hideAll();
3500        }else if(pm && pm.activeChild && active != m){
3501            pm.activeChild.hide();
3502        }
3503    }
3504
3505    // private this should really trigger on mouseup..
3506    function onMouseDown(e){
3507         Roo.log("on Mouse Up");
3508         
3509         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3510             Roo.log("MenuManager hideAll");
3511             hideAll();
3512             e.stopEvent();
3513         }
3514         
3515         
3516    }
3517
3518    // private
3519    function onBeforeCheck(mi, state){
3520        if(state){
3521            var g = groups[mi.group];
3522            for(var i = 0, l = g.length; i < l; i++){
3523                if(g[i] != mi){
3524                    g[i].setChecked(false);
3525                }
3526            }
3527        }
3528    }
3529
3530    return {
3531
3532        /**
3533         * Hides all menus that are currently visible
3534         */
3535        hideAll : function(){
3536             hideAll();  
3537        },
3538
3539        // private
3540        register : function(menu){
3541            if(!menus){
3542                init();
3543            }
3544            menus[menu.id] = menu;
3545            menu.on("beforehide", onBeforeHide);
3546            menu.on("hide", onHide);
3547            menu.on("beforeshow", onBeforeShow);
3548            menu.on("show", onShow);
3549            var g = menu.group;
3550            if(g && menu.events["checkchange"]){
3551                if(!groups[g]){
3552                    groups[g] = [];
3553                }
3554                groups[g].push(menu);
3555                menu.on("checkchange", onCheck);
3556            }
3557        },
3558
3559         /**
3560          * Returns a {@link Roo.menu.Menu} object
3561          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3562          * be used to generate and return a new Menu instance.
3563          */
3564        get : function(menu){
3565            if(typeof menu == "string"){ // menu id
3566                return menus[menu];
3567            }else if(menu.events){  // menu instance
3568                return menu;
3569            }
3570            /*else if(typeof menu.length == 'number'){ // array of menu items?
3571                return new Roo.bootstrap.Menu({items:menu});
3572            }else{ // otherwise, must be a config
3573                return new Roo.bootstrap.Menu(menu);
3574            }
3575            */
3576            return false;
3577        },
3578
3579        // private
3580        unregister : function(menu){
3581            delete menus[menu.id];
3582            menu.un("beforehide", onBeforeHide);
3583            menu.un("hide", onHide);
3584            menu.un("beforeshow", onBeforeShow);
3585            menu.un("show", onShow);
3586            var g = menu.group;
3587            if(g && menu.events["checkchange"]){
3588                groups[g].remove(menu);
3589                menu.un("checkchange", onCheck);
3590            }
3591        },
3592
3593        // private
3594        registerCheckable : function(menuItem){
3595            var g = menuItem.group;
3596            if(g){
3597                if(!groups[g]){
3598                    groups[g] = [];
3599                }
3600                groups[g].push(menuItem);
3601                menuItem.on("beforecheckchange", onBeforeCheck);
3602            }
3603        },
3604
3605        // private
3606        unregisterCheckable : function(menuItem){
3607            var g = menuItem.group;
3608            if(g){
3609                groups[g].remove(menuItem);
3610                menuItem.un("beforecheckchange", onBeforeCheck);
3611            }
3612        }
3613    };
3614 }(); 
3615 /**
3616  * @class Roo.bootstrap.menu.Menu
3617  * @extends Roo.bootstrap.Component
3618  * @licence LGPL
3619  * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3620  * @parent none
3621  * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3622  * 
3623  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3624  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3625  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3626  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3627 * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3628 * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3629  
3630  * @constructor
3631  * Create a new Menu
3632  * @param {Object} config The config objectQ
3633  */
3634
3635
3636 Roo.bootstrap.menu.Menu = function(config){
3637     
3638     if (config.type == 'treeview') {
3639         // normally menu's are drawn attached to the document to handle layering etc..
3640         // however treeview (used by the docs menu is drawn into the parent element)
3641         this.container_method = 'getChildContainer'; 
3642     }
3643     
3644     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3645     if (this.registerMenu && this.type != 'treeview')  {
3646         Roo.bootstrap.menu.Manager.register(this);
3647     }
3648     
3649     
3650     this.addEvents({
3651         /**
3652          * @event beforeshow
3653          * Fires before this menu is displayed (return false to block)
3654          * @param {Roo.menu.Menu} this
3655          */
3656         beforeshow : true,
3657         /**
3658          * @event beforehide
3659          * Fires before this menu is hidden (return false to block)
3660          * @param {Roo.menu.Menu} this
3661          */
3662         beforehide : true,
3663         /**
3664          * @event show
3665          * Fires after this menu is displayed
3666          * @param {Roo.menu.Menu} this
3667          */
3668         show : true,
3669         /**
3670          * @event hide
3671          * Fires after this menu is hidden
3672          * @param {Roo.menu.Menu} this
3673          */
3674         hide : true,
3675         /**
3676          * @event click
3677          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3678          * @param {Roo.menu.Menu} this
3679          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3680          * @param {Roo.EventObject} e
3681          */
3682         click : true,
3683         /**
3684          * @event mouseover
3685          * Fires when the mouse is hovering over this menu
3686          * @param {Roo.menu.Menu} this
3687          * @param {Roo.EventObject} e
3688          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3689          */
3690         mouseover : true,
3691         /**
3692          * @event mouseout
3693          * Fires when the mouse exits this menu
3694          * @param {Roo.menu.Menu} this
3695          * @param {Roo.EventObject} e
3696          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3697          */
3698         mouseout : true,
3699         /**
3700          * @event itemclick
3701          * Fires when a menu item contained in this menu is clicked
3702          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3703          * @param {Roo.EventObject} e
3704          */
3705         itemclick: true
3706     });
3707     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3708 };
3709
3710 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
3711     
3712    /// html : false,
3713    
3714     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3715     type: false,
3716     /**
3717      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3718      */
3719     registerMenu : true,
3720     
3721     menuItems :false, // stores the menu items..
3722     
3723     hidden:true,
3724         
3725     parentMenu : false,
3726     
3727     stopEvent : true,
3728     
3729     isLink : false,
3730     
3731     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3732     
3733     hideTrigger : false,
3734     
3735     align : 'tl-bl?',
3736     
3737     
3738     getChildContainer : function() {
3739         return this.el;  
3740     },
3741     
3742     getAutoCreate : function(){
3743          
3744         //if (['right'].indexOf(this.align)!==-1) {
3745         //    cfg.cn[1].cls += ' pull-right'
3746         //}
3747          
3748         var cfg = {
3749             tag : 'ul',
3750             cls : 'dropdown-menu shadow' ,
3751             style : 'z-index:1000'
3752             
3753         };
3754         
3755         if (this.type === 'submenu') {
3756             cfg.cls = 'submenu active';
3757         }
3758         if (this.type === 'treeview') {
3759             cfg.cls = 'treeview-menu';
3760         }
3761         
3762         return cfg;
3763     },
3764     initEvents : function() {
3765         
3766        // Roo.log("ADD event");
3767        // Roo.log(this.triggerEl.dom);
3768         if (this.triggerEl) {
3769             
3770             this.triggerEl.on('click', this.onTriggerClick, this);
3771             
3772             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3773             
3774             if (!this.hideTrigger) {
3775                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3776                     // dropdown toggle on the 'a' in BS4?
3777                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3778                 } else {
3779                     this.triggerEl.addClass('dropdown-toggle');
3780                 }
3781             }
3782         }
3783         
3784         if (Roo.isTouch) {
3785             this.el.on('touchstart'  , this.onTouch, this);
3786         }
3787         this.el.on('click' , this.onClick, this);
3788
3789         this.el.on("mouseover", this.onMouseOver, this);
3790         this.el.on("mouseout", this.onMouseOut, this);
3791         
3792     },
3793     
3794     findTargetItem : function(e)
3795     {
3796         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3797         if(!t){
3798             return false;
3799         }
3800         //Roo.log(t);         Roo.log(t.id);
3801         if(t && t.id){
3802             //Roo.log(this.menuitems);
3803             return this.menuitems.get(t.id);
3804             
3805             //return this.items.get(t.menuItemId);
3806         }
3807         
3808         return false;
3809     },
3810     
3811     onTouch : function(e) 
3812     {
3813         Roo.log("menu.onTouch");
3814         //e.stopEvent(); this make the user popdown broken
3815         this.onClick(e);
3816     },
3817     
3818     onClick : function(e)
3819     {
3820         Roo.log("menu.onClick");
3821         
3822         var t = this.findTargetItem(e);
3823         if(!t || t.isContainer){
3824             return;
3825         }
3826         Roo.log(e);
3827         /*
3828         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3829             if(t == this.activeItem && t.shouldDeactivate(e)){
3830                 this.activeItem.deactivate();
3831                 delete this.activeItem;
3832                 return;
3833             }
3834             if(t.canActivate){
3835                 this.setActiveItem(t, true);
3836             }
3837             return;
3838             
3839             
3840         }
3841         */
3842        
3843         Roo.log('pass click event');
3844         
3845         t.onClick(e);
3846         
3847         this.fireEvent("click", this, t, e);
3848         
3849         var _this = this;
3850         
3851         if(!t.href.length || t.href == '#'){
3852             (function() { _this.hide(); }).defer(100);
3853         }
3854         
3855     },
3856     
3857     onMouseOver : function(e){
3858         var t  = this.findTargetItem(e);
3859         //Roo.log(t);
3860         //if(t){
3861         //    if(t.canActivate && !t.disabled){
3862         //        this.setActiveItem(t, true);
3863         //    }
3864         //}
3865         
3866         this.fireEvent("mouseover", this, e, t);
3867     },
3868     isVisible : function(){
3869         return !this.hidden;
3870     },
3871     onMouseOut : function(e){
3872         var t  = this.findTargetItem(e);
3873         
3874         //if(t ){
3875         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3876         //        this.activeItem.deactivate();
3877         //        delete this.activeItem;
3878         //    }
3879         //}
3880         this.fireEvent("mouseout", this, e, t);
3881     },
3882     
3883     
3884     /**
3885      * Displays this menu relative to another element
3886      * @param {String/HTMLElement/Roo.Element} element The element to align to
3887      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3888      * the element (defaults to this.defaultAlign)
3889      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3890      */
3891     show : function(el, pos, parentMenu)
3892     {
3893         if (false === this.fireEvent("beforeshow", this)) {
3894             Roo.log("show canceled");
3895             return;
3896         }
3897         this.parentMenu = parentMenu;
3898         if(!this.el){
3899             this.render();
3900         }
3901         this.el.addClass('show'); // show otherwise we do not know how big we are..
3902          
3903         var xy = this.el.getAlignToXY(el, pos);
3904         
3905         // bl-tl << left align  below
3906         // tl-bl << left align 
3907         
3908         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3909             // if it goes to far to the right.. -> align left.
3910             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3911         }
3912         if(xy[0] < 0){
3913             // was left align - go right?
3914             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3915         }
3916         
3917         // goes down the bottom
3918         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3919            xy[1]  < 0 ){
3920             var a = this.align.replace('?', '').split('-');
3921             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3922             
3923         }
3924         
3925         this.showAt(  xy , parentMenu, false);
3926     },
3927      /**
3928      * Displays this menu at a specific xy position
3929      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3930      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3931      */
3932     showAt : function(xy, parentMenu, /* private: */_e){
3933         this.parentMenu = parentMenu;
3934         if(!this.el){
3935             this.render();
3936         }
3937         if(_e !== false){
3938             this.fireEvent("beforeshow", this);
3939             //xy = this.el.adjustForConstraints(xy);
3940         }
3941         
3942         //this.el.show();
3943         this.hideMenuItems();
3944         this.hidden = false;
3945         if (this.triggerEl) {
3946             this.triggerEl.addClass('open');
3947         }
3948         
3949         this.el.addClass('show');
3950         
3951         
3952         
3953         // reassign x when hitting right
3954         
3955         // reassign y when hitting bottom
3956         
3957         // but the list may align on trigger left or trigger top... should it be a properity?
3958         
3959         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3960             this.el.setXY(xy);
3961         }
3962         
3963         this.focus();
3964         this.fireEvent("show", this);
3965     },
3966     
3967     focus : function(){
3968         return;
3969         if(!this.hidden){
3970             this.doFocus.defer(50, this);
3971         }
3972     },
3973
3974     doFocus : function(){
3975         if(!this.hidden){
3976             this.focusEl.focus();
3977         }
3978     },
3979
3980     /**
3981      * Hides this menu and optionally all parent menus
3982      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3983      */
3984     hide : function(deep)
3985     {
3986         if (false === this.fireEvent("beforehide", this)) {
3987             Roo.log("hide canceled");
3988             return;
3989         }
3990         this.hideMenuItems();
3991         if(this.el && this.isVisible()){
3992            
3993             if(this.activeItem){
3994                 this.activeItem.deactivate();
3995                 this.activeItem = null;
3996             }
3997             if (this.triggerEl) {
3998                 this.triggerEl.removeClass('open');
3999             }
4000             
4001             this.el.removeClass('show');
4002             this.hidden = true;
4003             this.fireEvent("hide", this);
4004         }
4005         if(deep === true && this.parentMenu){
4006             this.parentMenu.hide(true);
4007         }
4008     },
4009     
4010     onTriggerClick : function(e)
4011     {
4012         Roo.log('trigger click');
4013         
4014         var target = e.getTarget();
4015         
4016         Roo.log(target.nodeName.toLowerCase());
4017         
4018         if(target.nodeName.toLowerCase() === 'i'){
4019             e.preventDefault();
4020         }
4021         
4022     },
4023     
4024     onTriggerPress  : function(e)
4025     {
4026         Roo.log('trigger press');
4027         //Roo.log(e.getTarget());
4028        // Roo.log(this.triggerEl.dom);
4029        
4030         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4031         var pel = Roo.get(e.getTarget());
4032         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4033             Roo.log('is treeview or dropdown?');
4034             return;
4035         }
4036         
4037         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4038             return;
4039         }
4040         
4041         if (this.isVisible()) {
4042             Roo.log('hide');
4043             this.hide();
4044         } else {
4045             Roo.log('show');
4046             
4047             this.show(this.triggerEl, this.align, false);
4048         }
4049         
4050         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4051             e.stopEvent();
4052         }
4053         
4054     },
4055        
4056     
4057     hideMenuItems : function()
4058     {
4059         Roo.log("hide Menu Items");
4060         if (!this.el) { 
4061             return;
4062         }
4063         
4064         this.el.select('.open',true).each(function(aa) {
4065             
4066             aa.removeClass('open');
4067          
4068         });
4069     },
4070     addxtypeChild : function (tree, cntr) {
4071         var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4072           
4073         this.menuitems.add(comp);
4074         return comp;
4075
4076     },
4077     getEl : function()
4078     {
4079         Roo.log(this.el);
4080         return this.el;
4081     },
4082     
4083     clear : function()
4084     {
4085         this.getEl().dom.innerHTML = '';
4086         this.menuitems.clear();
4087     }
4088 });
4089
4090  
4091  /**
4092  * @class Roo.bootstrap.menu.Item
4093  * @extends Roo.bootstrap.Component
4094  * @children  Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4095  * @parent Roo.bootstrap.menu.Menu
4096  * @licence LGPL
4097  * Bootstrap MenuItem class
4098  * 
4099  * @cfg {String} html the menu label
4100  * @cfg {String} href the link
4101  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4102  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4103  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4104  * @cfg {String} fa favicon to show on left of menu item.
4105  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4106  * 
4107  * 
4108  * @constructor
4109  * Create a new MenuItem
4110  * @param {Object} config The config object
4111  */
4112
4113
4114 Roo.bootstrap.menu.Item = function(config){
4115     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4116     this.addEvents({
4117         // raw events
4118         /**
4119          * @event click
4120          * The raw click event for the entire grid.
4121          * @param {Roo.bootstrap.menu.Item} this
4122          * @param {Roo.EventObject} e
4123          */
4124         "click" : true
4125     });
4126 };
4127
4128 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
4129     
4130     href : false,
4131     html : false,
4132     preventDefault: false,
4133     isContainer : false,
4134     active : false,
4135     fa: false,
4136     
4137     getAutoCreate : function(){
4138         
4139         if(this.isContainer){
4140             return {
4141                 tag: 'li',
4142                 cls: 'dropdown-menu-item '
4143             };
4144         }
4145         var ctag = {
4146             tag: 'span',
4147             html: 'Link'
4148         };
4149         
4150         var anc = {
4151             tag : 'a',
4152             cls : 'dropdown-item',
4153             href : '#',
4154             cn : [  ]
4155         };
4156         
4157         if (this.fa !== false) {
4158             anc.cn.push({
4159                 tag : 'i',
4160                 cls : 'fa fa-' + this.fa
4161             });
4162         }
4163         
4164         anc.cn.push(ctag);
4165         
4166         
4167         var cfg= {
4168             tag: 'li',
4169             cls: 'dropdown-menu-item',
4170             cn: [ anc ]
4171         };
4172         if (this.parent().type == 'treeview') {
4173             cfg.cls = 'treeview-menu';
4174         }
4175         if (this.active) {
4176             cfg.cls += ' active';
4177         }
4178         
4179         
4180         
4181         anc.href = this.href || cfg.cn[0].href ;
4182         ctag.html = this.html || cfg.cn[0].html ;
4183         return cfg;
4184     },
4185     
4186     initEvents: function()
4187     {
4188         if (this.parent().type == 'treeview') {
4189             this.el.select('a').on('click', this.onClick, this);
4190         }
4191         
4192         if (this.menu) {
4193             this.menu.parentType = this.xtype;
4194             this.menu.triggerEl = this.el;
4195             this.menu = this.addxtype(Roo.apply({}, this.menu));
4196         }
4197         
4198     },
4199     onClick : function(e)
4200     {
4201         //Roo.log('item on click ');
4202         
4203         if(this.href === false || this.preventDefault){
4204             e.preventDefault();
4205         }
4206         //this.parent().hideMenuItems();
4207         
4208         this.fireEvent('click', this, e);
4209     },
4210     getEl : function()
4211     {
4212         return this.el;
4213     } 
4214 });
4215
4216  
4217
4218  
4219
4220   
4221 /**
4222  * @class Roo.bootstrap.menu.Separator
4223  * @extends Roo.bootstrap.Component
4224  * @licence LGPL
4225  * @parent Roo.bootstrap.menu.Menu
4226  * Bootstrap Separator class
4227  * 
4228  * @constructor
4229  * Create a new Separator
4230  * @param {Object} config The config object
4231  */
4232
4233
4234 Roo.bootstrap.menu.Separator = function(config){
4235     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4236 };
4237
4238 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
4239     
4240     getAutoCreate : function(){
4241         var cfg = {
4242             tag : 'li',
4243             cls: 'dropdown-divider divider'
4244         };
4245         
4246         return cfg;
4247     }
4248    
4249 });
4250
4251  
4252
4253  
4254 /*
4255 * Licence: LGPL
4256 */
4257
4258 /**
4259  * @class Roo.bootstrap.Modal
4260  * @extends Roo.bootstrap.Component
4261  * @parent none builder
4262  * @children Roo.bootstrap.Component
4263  * Bootstrap Modal class
4264  * @cfg {String} title Title of dialog
4265  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4266  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4267  * @cfg {Boolean} specificTitle default false
4268  * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4269  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4270  * @cfg {Boolean} animate default true
4271  * @cfg {Boolean} allow_close default true
4272  * @cfg {Boolean} fitwindow default false
4273  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4274  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4275  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4276  * @cfg {String} size (sm|lg|xl) default empty
4277  * @cfg {Number} max_width set the max width of modal
4278  * @cfg {Boolean} editableTitle can the title be edited
4279
4280  *
4281  *
4282  * @constructor
4283  * Create a new Modal Dialog
4284  * @param {Object} config The config object
4285  */
4286
4287 Roo.bootstrap.Modal = function(config){
4288     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4289     this.addEvents({
4290         // raw events
4291         /**
4292          * @event btnclick
4293          * The raw btnclick event for the button
4294          * @param {Roo.EventObject} e
4295          */
4296         "btnclick" : true,
4297         /**
4298          * @event resize
4299          * Fire when dialog resize
4300          * @param {Roo.bootstrap.Modal} this
4301          * @param {Roo.EventObject} e
4302          */
4303         "resize" : true,
4304         /**
4305          * @event titlechanged
4306          * Fire when the editable title has been changed
4307          * @param {Roo.bootstrap.Modal} this
4308          * @param {Roo.EventObject} value
4309          */
4310         "titlechanged" : true 
4311         
4312     });
4313     this.buttons = this.buttons || [];
4314
4315     if (this.tmpl) {
4316         this.tmpl = Roo.factory(this.tmpl);
4317     }
4318
4319 };
4320
4321 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4322
4323     title : 'test dialog',
4324
4325     buttons : false,
4326
4327     // set on load...
4328
4329     html: false,
4330
4331     tmp: false,
4332
4333     specificTitle: false,
4334
4335     buttonPosition: 'right',
4336
4337     allow_close : true,
4338
4339     animate : true,
4340
4341     fitwindow: false,
4342     
4343      // private
4344     dialogEl: false,
4345     bodyEl:  false,
4346     footerEl:  false,
4347     titleEl:  false,
4348     closeEl:  false,
4349
4350     size: '',
4351     
4352     max_width: 0,
4353     
4354     max_height: 0,
4355     
4356     fit_content: false,
4357     editableTitle  : false,
4358
4359     onRender : function(ct, position)
4360     {
4361         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4362
4363         if(!this.el){
4364             var cfg = Roo.apply({},  this.getAutoCreate());
4365             cfg.id = Roo.id();
4366             //if(!cfg.name){
4367             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4368             //}
4369             //if (!cfg.name.length) {
4370             //    delete cfg.name;
4371            // }
4372             if (this.cls) {
4373                 cfg.cls += ' ' + this.cls;
4374             }
4375             if (this.style) {
4376                 cfg.style = this.style;
4377             }
4378             this.el = Roo.get(document.body).createChild(cfg, position);
4379         }
4380         //var type = this.el.dom.type;
4381
4382
4383         if(this.tabIndex !== undefined){
4384             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4385         }
4386
4387         this.dialogEl = this.el.select('.modal-dialog',true).first();
4388         this.bodyEl = this.el.select('.modal-body',true).first();
4389         this.closeEl = this.el.select('.modal-header .close', true).first();
4390         this.headerEl = this.el.select('.modal-header',true).first();
4391         this.titleEl = this.el.select('.modal-title',true).first();
4392         this.footerEl = this.el.select('.modal-footer',true).first();
4393
4394         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4395         
4396         //this.el.addClass("x-dlg-modal");
4397
4398         if (this.buttons.length) {
4399             Roo.each(this.buttons, function(bb) {
4400                 var b = Roo.apply({}, bb);
4401                 b.xns = b.xns || Roo.bootstrap;
4402                 b.xtype = b.xtype || 'Button';
4403                 if (typeof(b.listeners) == 'undefined') {
4404                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4405                 }
4406
4407                 var btn = Roo.factory(b);
4408
4409                 btn.render(this.getButtonContainer());
4410
4411             },this);
4412         }
4413         // render the children.
4414         var nitems = [];
4415
4416         if(typeof(this.items) != 'undefined'){
4417             var items = this.items;
4418             delete this.items;
4419
4420             for(var i =0;i < items.length;i++) {
4421                 // we force children not to montor widnow resize  - as we do that for them.
4422                 items[i].monitorWindowResize = false;
4423                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4424             }
4425         }
4426
4427         this.items = nitems;
4428
4429         // where are these used - they used to be body/close/footer
4430
4431
4432         this.initEvents();
4433         //this.el.addClass([this.fieldClass, this.cls]);
4434
4435     },
4436
4437     getAutoCreate : function()
4438     {
4439         // we will default to modal-body-overflow - might need to remove or make optional later.
4440         var bdy = {
4441                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4442                 html : this.html || ''
4443         };
4444
4445         var title = {
4446             tag: 'h5',
4447             cls : 'modal-title',
4448             html : this.title
4449         };
4450
4451         if(this.specificTitle){ // WTF is this?
4452             title = this.title;
4453         }
4454
4455         var header = [];
4456         if (this.allow_close && Roo.bootstrap.version == 3) {
4457             header.push({
4458                 tag: 'button',
4459                 cls : 'close',
4460                 html : '&times'
4461             });
4462         }
4463
4464         header.push(title);
4465
4466         if (this.editableTitle) {
4467             header.push({
4468                 cls: 'form-control roo-editable-title d-none',
4469                 tag: 'input',
4470                 type: 'text'
4471             });
4472         }
4473         
4474         if (this.allow_close && Roo.bootstrap.version == 4) {
4475             header.push({
4476                 tag: 'button',
4477                 cls : 'close',
4478                 html : '&times'
4479             });
4480         }
4481         
4482         var size = '';
4483
4484         if(this.size.length){
4485             size = 'modal-' + this.size;
4486         }
4487         
4488         var footer = Roo.bootstrap.version == 3 ?
4489             {
4490                 cls : 'modal-footer',
4491                 cn : [
4492                     {
4493                         tag: 'div',
4494                         cls: 'btn-' + this.buttonPosition
4495                     }
4496                 ]
4497
4498             } :
4499             {  // BS4 uses mr-auto on left buttons....
4500                 cls : 'modal-footer'
4501             };
4502
4503             
4504
4505         
4506         
4507         var modal = {
4508             cls: "modal",
4509              cn : [
4510                 {
4511                     cls: "modal-dialog " + size,
4512                     cn : [
4513                         {
4514                             cls : "modal-content",
4515                             cn : [
4516                                 {
4517                                     cls : 'modal-header',
4518                                     cn : header
4519                                 },
4520                                 bdy,
4521                                 footer
4522                             ]
4523
4524                         }
4525                     ]
4526
4527                 }
4528             ]
4529         };
4530
4531         if(this.animate){
4532             modal.cls += ' fade';
4533         }
4534
4535         return modal;
4536
4537     },
4538     getChildContainer : function() {
4539
4540          return this.bodyEl;
4541
4542     },
4543     getButtonContainer : function() {
4544         
4545          return Roo.bootstrap.version == 4 ?
4546             this.el.select('.modal-footer',true).first()
4547             : this.el.select('.modal-footer div',true).first();
4548
4549     },
4550     initEvents : function()
4551     {
4552         if (this.allow_close) {
4553             this.closeEl.on('click', this.hide, this);
4554         }
4555         Roo.EventManager.onWindowResize(this.resize, this, true);
4556         if (this.editableTitle) {
4557             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4558             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4559             this.headerEditEl.on('keyup', function(e) {
4560                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4561                         this.toggleHeaderInput(false)
4562                     }
4563                 }, this);
4564             this.headerEditEl.on('blur', function(e) {
4565                 this.toggleHeaderInput(false)
4566             },this);
4567         }
4568
4569     },
4570   
4571
4572     resize : function()
4573     {
4574         this.maskEl.setSize(
4575             Roo.lib.Dom.getViewWidth(true),
4576             Roo.lib.Dom.getViewHeight(true)
4577         );
4578         
4579         if (this.fitwindow) {
4580             
4581            this.dialogEl.setStyle( { 'max-width' : '100%' });
4582             this.setSize(
4583                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4584                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4585             );
4586             return;
4587         }
4588         
4589         if(this.max_width !== 0) {
4590             
4591             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4592             
4593             if(this.height) {
4594                 this.setSize(w, this.height);
4595                 return;
4596             }
4597             
4598             if(this.max_height) {
4599                 this.setSize(w,Math.min(
4600                     this.max_height,
4601                     Roo.lib.Dom.getViewportHeight(true) - 60
4602                 ));
4603                 
4604                 return;
4605             }
4606             
4607             if(!this.fit_content) {
4608                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4609                 return;
4610             }
4611             
4612             this.setSize(w, Math.min(
4613                 60 +
4614                 this.headerEl.getHeight() + 
4615                 this.footerEl.getHeight() + 
4616                 this.getChildHeight(this.bodyEl.dom.childNodes),
4617                 Roo.lib.Dom.getViewportHeight(true) - 60)
4618             );
4619         }
4620         
4621     },
4622
4623     setSize : function(w,h)
4624     {
4625         if (!w && !h) {
4626             return;
4627         }
4628         
4629         this.resizeTo(w,h);
4630         // any layout/border etc.. resize..
4631         (function () {
4632             this.items.forEach( function(e) {
4633                 e.layout ? e.layout() : false;
4634
4635             });
4636         }).defer(100,this);
4637         
4638     },
4639
4640     show : function() {
4641
4642         if (!this.rendered) {
4643             this.render();
4644         }
4645         this.toggleHeaderInput(false);
4646         //this.el.setStyle('display', 'block');
4647         this.el.removeClass('hideing');
4648         this.el.dom.style.display='block';
4649         
4650         Roo.get(document.body).addClass('modal-open');
4651  
4652         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4653             
4654             (function(){
4655                 this.el.addClass('show');
4656                 this.el.addClass('in');
4657             }).defer(50, this);
4658         }else{
4659             this.el.addClass('show');
4660             this.el.addClass('in');
4661         }
4662
4663         // not sure how we can show data in here..
4664         //if (this.tmpl) {
4665         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4666         //}
4667
4668         Roo.get(document.body).addClass("x-body-masked");
4669         
4670         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4671         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4672         this.maskEl.dom.style.display = 'block';
4673         this.maskEl.addClass('show');
4674         
4675         
4676         this.resize();
4677         
4678         this.fireEvent('show', this);
4679
4680         // set zindex here - otherwise it appears to be ignored...
4681         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4682         
4683         
4684         // this is for children that are... layout.Border 
4685         (function () {
4686             this.items.forEach( function(e) {
4687                 e.layout ? e.layout() : false;
4688
4689             });
4690         }).defer(100,this);
4691
4692     },
4693     hide : function()
4694     {
4695         if(this.fireEvent("beforehide", this) !== false){
4696             
4697             this.maskEl.removeClass('show');
4698             
4699             this.maskEl.dom.style.display = '';
4700             Roo.get(document.body).removeClass("x-body-masked");
4701             this.el.removeClass('in');
4702             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4703
4704             if(this.animate){ // why
4705                 this.el.addClass('hideing');
4706                 this.el.removeClass('show');
4707                 (function(){
4708                     if (!this.el.hasClass('hideing')) {
4709                         return; // it's been shown again...
4710                     }
4711                     
4712                     this.el.dom.style.display='';
4713
4714                     Roo.get(document.body).removeClass('modal-open');
4715                     this.el.removeClass('hideing');
4716                 }).defer(150,this);
4717                 
4718             }else{
4719                 this.el.removeClass('show');
4720                 this.el.dom.style.display='';
4721                 Roo.get(document.body).removeClass('modal-open');
4722
4723             }
4724             this.fireEvent('hide', this);
4725         }
4726     },
4727     isVisible : function()
4728     {
4729         
4730         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4731         
4732     },
4733
4734     addButton : function(str, cb)
4735     {
4736
4737
4738         var b = Roo.apply({}, { html : str } );
4739         b.xns = b.xns || Roo.bootstrap;
4740         b.xtype = b.xtype || 'Button';
4741         if (typeof(b.listeners) == 'undefined') {
4742             b.listeners = { click : cb.createDelegate(this)  };
4743         }
4744
4745         var btn = Roo.factory(b);
4746
4747         btn.render(this.getButtonContainer());
4748
4749         return btn;
4750
4751     },
4752
4753     setDefaultButton : function(btn)
4754     {
4755         //this.el.select('.modal-footer').()
4756     },
4757
4758     resizeTo: function(w,h)
4759     {
4760         this.dialogEl.setWidth(w);
4761         
4762         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4763
4764         this.bodyEl.setHeight(h - diff);
4765         
4766         this.fireEvent('resize', this);
4767     },
4768     
4769     setContentSize  : function(w, h)
4770     {
4771
4772     },
4773     onButtonClick: function(btn,e)
4774     {
4775         //Roo.log([a,b,c]);
4776         this.fireEvent('btnclick', btn.name, e);
4777     },
4778      /**
4779      * Set the title of the Dialog
4780      * @param {String} str new Title
4781      */
4782     setTitle: function(str) {
4783         this.titleEl.dom.innerHTML = str;
4784         this.title = str;
4785     },
4786     /**
4787      * Set the body of the Dialog
4788      * @param {String} str new Title
4789      */
4790     setBody: function(str) {
4791         this.bodyEl.dom.innerHTML = str;
4792     },
4793     /**
4794      * Set the body of the Dialog using the template
4795      * @param {Obj} data - apply this data to the template and replace the body contents.
4796      */
4797     applyBody: function(obj)
4798     {
4799         if (!this.tmpl) {
4800             Roo.log("Error - using apply Body without a template");
4801             //code
4802         }
4803         this.tmpl.overwrite(this.bodyEl, obj);
4804     },
4805     
4806     getChildHeight : function(child_nodes)
4807     {
4808         if(
4809             !child_nodes ||
4810             child_nodes.length == 0
4811         ) {
4812             return 0;
4813         }
4814         
4815         var child_height = 0;
4816         
4817         for(var i = 0; i < child_nodes.length; i++) {
4818             
4819             /*
4820             * for modal with tabs...
4821             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4822                 
4823                 var layout_childs = child_nodes[i].childNodes;
4824                 
4825                 for(var j = 0; j < layout_childs.length; j++) {
4826                     
4827                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4828                         
4829                         var layout_body_childs = layout_childs[j].childNodes;
4830                         
4831                         for(var k = 0; k < layout_body_childs.length; k++) {
4832                             
4833                             if(layout_body_childs[k].classList.contains('navbar')) {
4834                                 child_height += layout_body_childs[k].offsetHeight;
4835                                 continue;
4836                             }
4837                             
4838                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4839                                 
4840                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4841                                 
4842                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4843                                     
4844                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4845                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4846                                         continue;
4847                                     }
4848                                     
4849                                 }
4850                                 
4851                             }
4852                             
4853                         }
4854                     }
4855                 }
4856                 continue;
4857             }
4858             */
4859             
4860             child_height += child_nodes[i].offsetHeight;
4861             // Roo.log(child_nodes[i].offsetHeight);
4862         }
4863         
4864         return child_height;
4865     },
4866     toggleHeaderInput : function(is_edit)
4867     {
4868         if (!this.editableTitle) {
4869             return; // not editable.
4870         }
4871         if (is_edit && this.is_header_editing) {
4872             return; // already editing..
4873         }
4874         if (is_edit) {
4875     
4876             this.headerEditEl.dom.value = this.title;
4877             this.headerEditEl.removeClass('d-none');
4878             this.headerEditEl.dom.focus();
4879             this.titleEl.addClass('d-none');
4880             
4881             this.is_header_editing = true;
4882             return
4883         }
4884         // flip back to not editing.
4885         this.title = this.headerEditEl.dom.value;
4886         this.headerEditEl.addClass('d-none');
4887         this.titleEl.removeClass('d-none');
4888         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4889         this.is_header_editing = false;
4890         this.fireEvent('titlechanged', this, this.title);
4891     
4892             
4893         
4894     }
4895
4896 });
4897
4898
4899 Roo.apply(Roo.bootstrap.Modal,  {
4900     /**
4901          * Button config that displays a single OK button
4902          * @type Object
4903          */
4904         OK :  [{
4905             name : 'ok',
4906             weight : 'primary',
4907             html : 'OK'
4908         }],
4909         /**
4910          * Button config that displays Yes and No buttons
4911          * @type Object
4912          */
4913         YESNO : [
4914             {
4915                 name  : 'no',
4916                 html : 'No'
4917             },
4918             {
4919                 name  :'yes',
4920                 weight : 'primary',
4921                 html : 'Yes'
4922             }
4923         ],
4924
4925         /**
4926          * Button config that displays OK and Cancel buttons
4927          * @type Object
4928          */
4929         OKCANCEL : [
4930             {
4931                name : 'cancel',
4932                 html : 'Cancel'
4933             },
4934             {
4935                 name : 'ok',
4936                 weight : 'primary',
4937                 html : 'OK'
4938             }
4939         ],
4940         /**
4941          * Button config that displays Yes, No and Cancel buttons
4942          * @type Object
4943          */
4944         YESNOCANCEL : [
4945             {
4946                 name : 'yes',
4947                 weight : 'primary',
4948                 html : 'Yes'
4949             },
4950             {
4951                 name : 'no',
4952                 html : 'No'
4953             },
4954             {
4955                 name : 'cancel',
4956                 html : 'Cancel'
4957             }
4958         ],
4959         
4960         zIndex : 10001
4961 });
4962
4963 /*
4964  * - LGPL
4965  *
4966  * messagebox - can be used as a replace
4967  * 
4968  */
4969 /**
4970  * @class Roo.MessageBox
4971  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4972  * Example usage:
4973  *<pre><code>
4974 // Basic alert:
4975 Roo.Msg.alert('Status', 'Changes saved successfully.');
4976
4977 // Prompt for user data:
4978 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4979     if (btn == 'ok'){
4980         // process text value...
4981     }
4982 });
4983
4984 // Show a dialog using config options:
4985 Roo.Msg.show({
4986    title:'Save Changes?',
4987    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4988    buttons: Roo.Msg.YESNOCANCEL,
4989    fn: processResult,
4990    animEl: 'elId'
4991 });
4992 </code></pre>
4993  * @static
4994  */
4995 Roo.bootstrap.MessageBox = function(){
4996     var dlg, opt, mask, waitTimer;
4997     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4998     var buttons, activeTextEl, bwidth;
4999
5000     
5001     // private
5002     var handleButton = function(button){
5003         dlg.hide();
5004         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5005     };
5006
5007     // private
5008     var handleHide = function(){
5009         if(opt && opt.cls){
5010             dlg.el.removeClass(opt.cls);
5011         }
5012         //if(waitTimer){
5013         //    Roo.TaskMgr.stop(waitTimer);
5014         //    waitTimer = null;
5015         //}
5016     };
5017
5018     // private
5019     var updateButtons = function(b){
5020         var width = 0;
5021         if(!b){
5022             buttons["ok"].hide();
5023             buttons["cancel"].hide();
5024             buttons["yes"].hide();
5025             buttons["no"].hide();
5026             dlg.footerEl.hide();
5027             
5028             return width;
5029         }
5030         dlg.footerEl.show();
5031         for(var k in buttons){
5032             if(typeof buttons[k] != "function"){
5033                 if(b[k]){
5034                     buttons[k].show();
5035                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5036                     width += buttons[k].el.getWidth()+15;
5037                 }else{
5038                     buttons[k].hide();
5039                 }
5040             }
5041         }
5042         return width;
5043     };
5044
5045     // private
5046     var handleEsc = function(d, k, e){
5047         if(opt && opt.closable !== false){
5048             dlg.hide();
5049         }
5050         if(e){
5051             e.stopEvent();
5052         }
5053     };
5054
5055     return {
5056         /**
5057          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5058          * @return {Roo.BasicDialog} The BasicDialog element
5059          */
5060         getDialog : function(){
5061            if(!dlg){
5062                 dlg = new Roo.bootstrap.Modal( {
5063                     //draggable: true,
5064                     //resizable:false,
5065                     //constraintoviewport:false,
5066                     //fixedcenter:true,
5067                     //collapsible : false,
5068                     //shim:true,
5069                     //modal: true,
5070                 //    width: 'auto',
5071                   //  height:100,
5072                     //buttonAlign:"center",
5073                     closeClick : function(){
5074                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5075                             handleButton("no");
5076                         }else{
5077                             handleButton("cancel");
5078                         }
5079                     }
5080                 });
5081                 dlg.render();
5082                 dlg.on("hide", handleHide);
5083                 mask = dlg.mask;
5084                 //dlg.addKeyListener(27, handleEsc);
5085                 buttons = {};
5086                 this.buttons = buttons;
5087                 var bt = this.buttonText;
5088                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5089                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5090                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5091                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5092                 //Roo.log(buttons);
5093                 bodyEl = dlg.bodyEl.createChild({
5094
5095                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5096                         '<textarea class="roo-mb-textarea"></textarea>' +
5097                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5098                 });
5099                 msgEl = bodyEl.dom.firstChild;
5100                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5101                 textboxEl.enableDisplayMode();
5102                 textboxEl.addKeyListener([10,13], function(){
5103                     if(dlg.isVisible() && opt && opt.buttons){
5104                         if(opt.buttons.ok){
5105                             handleButton("ok");
5106                         }else if(opt.buttons.yes){
5107                             handleButton("yes");
5108                         }
5109                     }
5110                 });
5111                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5112                 textareaEl.enableDisplayMode();
5113                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5114                 progressEl.enableDisplayMode();
5115                 
5116                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5117                 var pf = progressEl.dom.firstChild;
5118                 if (pf) {
5119                     pp = Roo.get(pf.firstChild);
5120                     pp.setHeight(pf.offsetHeight);
5121                 }
5122                 
5123             }
5124             return dlg;
5125         },
5126
5127         /**
5128          * Updates the message box body text
5129          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5130          * the XHTML-compliant non-breaking space character '&amp;#160;')
5131          * @return {Roo.MessageBox} This message box
5132          */
5133         updateText : function(text)
5134         {
5135             if(!dlg.isVisible() && !opt.width){
5136                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5137                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5138             }
5139             msgEl.innerHTML = text || '&#160;';
5140       
5141             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5142             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5143             var w = Math.max(
5144                     Math.min(opt.width || cw , this.maxWidth), 
5145                     Math.max(opt.minWidth || this.minWidth, bwidth)
5146             );
5147             if(opt.prompt){
5148                 activeTextEl.setWidth(w);
5149             }
5150             if(dlg.isVisible()){
5151                 dlg.fixedcenter = false;
5152             }
5153             // to big, make it scroll. = But as usual stupid IE does not support
5154             // !important..
5155             
5156             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5157                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5158                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5159             } else {
5160                 bodyEl.dom.style.height = '';
5161                 bodyEl.dom.style.overflowY = '';
5162             }
5163             if (cw > w) {
5164                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5165             } else {
5166                 bodyEl.dom.style.overflowX = '';
5167             }
5168             
5169             dlg.setContentSize(w, bodyEl.getHeight());
5170             if(dlg.isVisible()){
5171                 dlg.fixedcenter = true;
5172             }
5173             return this;
5174         },
5175
5176         /**
5177          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5178          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5179          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5180          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5181          * @return {Roo.MessageBox} This message box
5182          */
5183         updateProgress : function(value, text){
5184             if(text){
5185                 this.updateText(text);
5186             }
5187             
5188             if (pp) { // weird bug on my firefox - for some reason this is not defined
5189                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5190                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5191             }
5192             return this;
5193         },        
5194
5195         /**
5196          * Returns true if the message box is currently displayed
5197          * @return {Boolean} True if the message box is visible, else false
5198          */
5199         isVisible : function(){
5200             return dlg && dlg.isVisible();  
5201         },
5202
5203         /**
5204          * Hides the message box if it is displayed
5205          */
5206         hide : function(){
5207             if(this.isVisible()){
5208                 dlg.hide();
5209             }  
5210         },
5211
5212         /**
5213          * Displays a new message box, or reinitializes an existing message box, based on the config options
5214          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5215          * The following config object properties are supported:
5216          * <pre>
5217 Property    Type             Description
5218 ----------  ---------------  ------------------------------------------------------------------------------------
5219 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5220                                    closes (defaults to undefined)
5221 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5222                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5223 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5224                                    progress and wait dialogs will ignore this property and always hide the
5225                                    close button as they can only be closed programmatically.
5226 cls               String           A custom CSS class to apply to the message box element
5227 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5228                                    displayed (defaults to 75)
5229 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5230                                    function will be btn (the name of the button that was clicked, if applicable,
5231                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5232                                    Progress and wait dialogs will ignore this option since they do not respond to
5233                                    user actions and can only be closed programmatically, so any required function
5234                                    should be called by the same code after it closes the dialog.
5235 icon              String           A CSS class that provides a background image to be used as an icon for
5236                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5237 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5238 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5239 modal             Boolean          False to allow user interaction with the page while the message box is
5240                                    displayed (defaults to true)
5241 msg               String           A string that will replace the existing message box body text (defaults
5242                                    to the XHTML-compliant non-breaking space character '&#160;')
5243 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5244 progress          Boolean          True to display a progress bar (defaults to false)
5245 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5246 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5247 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5248 title             String           The title text
5249 value             String           The string value to set into the active textbox element if displayed
5250 wait              Boolean          True to display a progress bar (defaults to false)
5251 width             Number           The width of the dialog in pixels
5252 </pre>
5253          *
5254          * Example usage:
5255          * <pre><code>
5256 Roo.Msg.show({
5257    title: 'Address',
5258    msg: 'Please enter your address:',
5259    width: 300,
5260    buttons: Roo.MessageBox.OKCANCEL,
5261    multiline: true,
5262    fn: saveAddress,
5263    animEl: 'addAddressBtn'
5264 });
5265 </code></pre>
5266          * @param {Object} config Configuration options
5267          * @return {Roo.MessageBox} This message box
5268          */
5269         show : function(options)
5270         {
5271             
5272             // this causes nightmares if you show one dialog after another
5273             // especially on callbacks..
5274              
5275             if(this.isVisible()){
5276                 
5277                 this.hide();
5278                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5279                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5280                 Roo.log("New Dialog Message:" +  options.msg )
5281                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5282                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5283                 
5284             }
5285             var d = this.getDialog();
5286             opt = options;
5287             d.setTitle(opt.title || "&#160;");
5288             d.closeEl.setDisplayed(opt.closable !== false);
5289             activeTextEl = textboxEl;
5290             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5291             if(opt.prompt){
5292                 if(opt.multiline){
5293                     textboxEl.hide();
5294                     textareaEl.show();
5295                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5296                         opt.multiline : this.defaultTextHeight);
5297                     activeTextEl = textareaEl;
5298                 }else{
5299                     textboxEl.show();
5300                     textareaEl.hide();
5301                 }
5302             }else{
5303                 textboxEl.hide();
5304                 textareaEl.hide();
5305             }
5306             progressEl.setDisplayed(opt.progress === true);
5307             if (opt.progress) {
5308                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5309             }
5310             this.updateProgress(0);
5311             activeTextEl.dom.value = opt.value || "";
5312             if(opt.prompt){
5313                 dlg.setDefaultButton(activeTextEl);
5314             }else{
5315                 var bs = opt.buttons;
5316                 var db = null;
5317                 if(bs && bs.ok){
5318                     db = buttons["ok"];
5319                 }else if(bs && bs.yes){
5320                     db = buttons["yes"];
5321                 }
5322                 dlg.setDefaultButton(db);
5323             }
5324             bwidth = updateButtons(opt.buttons);
5325             this.updateText(opt.msg);
5326             if(opt.cls){
5327                 d.el.addClass(opt.cls);
5328             }
5329             d.proxyDrag = opt.proxyDrag === true;
5330             d.modal = opt.modal !== false;
5331             d.mask = opt.modal !== false ? mask : false;
5332             if(!d.isVisible()){
5333                 // force it to the end of the z-index stack so it gets a cursor in FF
5334                 document.body.appendChild(dlg.el.dom);
5335                 d.animateTarget = null;
5336                 d.show(options.animEl);
5337             }
5338             return this;
5339         },
5340
5341         /**
5342          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5343          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5344          * and closing the message box when the process is complete.
5345          * @param {String} title The title bar text
5346          * @param {String} msg The message box body text
5347          * @return {Roo.MessageBox} This message box
5348          */
5349         progress : function(title, msg){
5350             this.show({
5351                 title : title,
5352                 msg : msg,
5353                 buttons: false,
5354                 progress:true,
5355                 closable:false,
5356                 minWidth: this.minProgressWidth,
5357                 modal : true
5358             });
5359             return this;
5360         },
5361
5362         /**
5363          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5364          * If a callback function is passed it will be called after the user clicks the button, and the
5365          * id of the button that was clicked will be passed as the only parameter to the callback
5366          * (could also be the top-right close button).
5367          * @param {String} title The title bar text
5368          * @param {String} msg The message box body text
5369          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5370          * @param {Object} scope (optional) The scope of the callback function
5371          * @return {Roo.MessageBox} This message box
5372          */
5373         alert : function(title, msg, fn, scope)
5374         {
5375             this.show({
5376                 title : title,
5377                 msg : msg,
5378                 buttons: this.OK,
5379                 fn: fn,
5380                 closable : false,
5381                 scope : scope,
5382                 modal : true
5383             });
5384             return this;
5385         },
5386
5387         /**
5388          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5389          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5390          * You are responsible for closing the message box when the process is complete.
5391          * @param {String} msg The message box body text
5392          * @param {String} title (optional) The title bar text
5393          * @return {Roo.MessageBox} This message box
5394          */
5395         wait : function(msg, title){
5396             this.show({
5397                 title : title,
5398                 msg : msg,
5399                 buttons: false,
5400                 closable:false,
5401                 progress:true,
5402                 modal:true,
5403                 width:300,
5404                 wait:true
5405             });
5406             waitTimer = Roo.TaskMgr.start({
5407                 run: function(i){
5408                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5409                 },
5410                 interval: 1000
5411             });
5412             return this;
5413         },
5414
5415         /**
5416          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5417          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5418          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5419          * @param {String} title The title bar text
5420          * @param {String} msg The message box body text
5421          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5422          * @param {Object} scope (optional) The scope of the callback function
5423          * @return {Roo.MessageBox} This message box
5424          */
5425         confirm : function(title, msg, fn, scope){
5426             this.show({
5427                 title : title,
5428                 msg : msg,
5429                 buttons: this.YESNO,
5430                 fn: fn,
5431                 scope : scope,
5432                 modal : true
5433             });
5434             return this;
5435         },
5436
5437         /**
5438          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5439          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5440          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5441          * (could also be the top-right close button) and the text that was entered will be passed as the two
5442          * parameters to the callback.
5443          * @param {String} title The title bar text
5444          * @param {String} msg The message box body text
5445          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5446          * @param {Object} scope (optional) The scope of the callback function
5447          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5448          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5449          * @return {Roo.MessageBox} This message box
5450          */
5451         prompt : function(title, msg, fn, scope, multiline){
5452             this.show({
5453                 title : title,
5454                 msg : msg,
5455                 buttons: this.OKCANCEL,
5456                 fn: fn,
5457                 minWidth:250,
5458                 scope : scope,
5459                 prompt:true,
5460                 multiline: multiline,
5461                 modal : true
5462             });
5463             return this;
5464         },
5465
5466         /**
5467          * Button config that displays a single OK button
5468          * @type Object
5469          */
5470         OK : {ok:true},
5471         /**
5472          * Button config that displays Yes and No buttons
5473          * @type Object
5474          */
5475         YESNO : {yes:true, no:true},
5476         /**
5477          * Button config that displays OK and Cancel buttons
5478          * @type Object
5479          */
5480         OKCANCEL : {ok:true, cancel:true},
5481         /**
5482          * Button config that displays Yes, No and Cancel buttons
5483          * @type Object
5484          */
5485         YESNOCANCEL : {yes:true, no:true, cancel:true},
5486
5487         /**
5488          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5489          * @type Number
5490          */
5491         defaultTextHeight : 75,
5492         /**
5493          * The maximum width in pixels of the message box (defaults to 600)
5494          * @type Number
5495          */
5496         maxWidth : 600,
5497         /**
5498          * The minimum width in pixels of the message box (defaults to 100)
5499          * @type Number
5500          */
5501         minWidth : 100,
5502         /**
5503          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5504          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5505          * @type Number
5506          */
5507         minProgressWidth : 250,
5508         /**
5509          * An object containing the default button text strings that can be overriden for localized language support.
5510          * Supported properties are: ok, cancel, yes and no.
5511          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5512          * @type Object
5513          */
5514         buttonText : {
5515             ok : "OK",
5516             cancel : "Cancel",
5517             yes : "Yes",
5518             no : "No"
5519         }
5520     };
5521 }();
5522
5523 /**
5524  * Shorthand for {@link Roo.MessageBox}
5525  */
5526 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5527 Roo.Msg = Roo.Msg || Roo.MessageBox;
5528 /*
5529  * - LGPL
5530  *
5531  * navbar
5532  * 
5533  */
5534
5535 /**
5536  * @class Roo.bootstrap.nav.Bar
5537  * @extends Roo.bootstrap.Component
5538  * @abstract
5539  * Bootstrap Navbar class
5540
5541  * @constructor
5542  * Create a new Navbar
5543  * @param {Object} config The config object
5544  */
5545
5546
5547 Roo.bootstrap.nav.Bar = function(config){
5548     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5549     this.addEvents({
5550         // raw events
5551         /**
5552          * @event beforetoggle
5553          * Fire before toggle the menu
5554          * @param {Roo.EventObject} e
5555          */
5556         "beforetoggle" : true
5557     });
5558 };
5559
5560 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5561     
5562     
5563    
5564     // private
5565     navItems : false,
5566     loadMask : false,
5567     
5568     
5569     getAutoCreate : function(){
5570         
5571         
5572         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5573         
5574     },
5575     
5576     initEvents :function ()
5577     {
5578         //Roo.log(this.el.select('.navbar-toggle',true));
5579         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5580         
5581         var mark = {
5582             tag: "div",
5583             cls:"x-dlg-mask"
5584         };
5585         
5586         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5587         
5588         var size = this.el.getSize();
5589         this.maskEl.setSize(size.width, size.height);
5590         this.maskEl.enableDisplayMode("block");
5591         this.maskEl.hide();
5592         
5593         if(this.loadMask){
5594             this.maskEl.show();
5595         }
5596     },
5597     
5598     
5599     getChildContainer : function()
5600     {
5601         if (this.el && this.el.select('.collapse').getCount()) {
5602             return this.el.select('.collapse',true).first();
5603         }
5604         
5605         return this.el;
5606     },
5607     
5608     mask : function()
5609     {
5610         this.maskEl.show();
5611     },
5612     
5613     unmask : function()
5614     {
5615         this.maskEl.hide();
5616     },
5617     onToggle : function()
5618     {
5619         
5620         if(this.fireEvent('beforetoggle', this) === false){
5621             return;
5622         }
5623         var ce = this.el.select('.navbar-collapse',true).first();
5624       
5625         if (!ce.hasClass('show')) {
5626            this.expand();
5627         } else {
5628             this.collapse();
5629         }
5630         
5631         
5632     
5633     },
5634     /**
5635      * Expand the navbar pulldown 
5636      */
5637     expand : function ()
5638     {
5639        
5640         var ce = this.el.select('.navbar-collapse',true).first();
5641         if (ce.hasClass('collapsing')) {
5642             return;
5643         }
5644         ce.dom.style.height = '';
5645                // show it...
5646         ce.addClass('in'); // old...
5647         ce.removeClass('collapse');
5648         ce.addClass('show');
5649         var h = ce.getHeight();
5650         Roo.log(h);
5651         ce.removeClass('show');
5652         // at this point we should be able to see it..
5653         ce.addClass('collapsing');
5654         
5655         ce.setHeight(0); // resize it ...
5656         ce.on('transitionend', function() {
5657             //Roo.log('done transition');
5658             ce.removeClass('collapsing');
5659             ce.addClass('show');
5660             ce.removeClass('collapse');
5661
5662             ce.dom.style.height = '';
5663         }, this, { single: true} );
5664         ce.setHeight(h);
5665         ce.dom.scrollTop = 0;
5666     },
5667     /**
5668      * Collapse the navbar pulldown 
5669      */
5670     collapse : function()
5671     {
5672          var ce = this.el.select('.navbar-collapse',true).first();
5673        
5674         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5675             // it's collapsed or collapsing..
5676             return;
5677         }
5678         ce.removeClass('in'); // old...
5679         ce.setHeight(ce.getHeight());
5680         ce.removeClass('show');
5681         ce.addClass('collapsing');
5682         
5683         ce.on('transitionend', function() {
5684             ce.dom.style.height = '';
5685             ce.removeClass('collapsing');
5686             ce.addClass('collapse');
5687         }, this, { single: true} );
5688         ce.setHeight(0);
5689     }
5690     
5691     
5692     
5693 });
5694
5695
5696
5697  
5698
5699  /*
5700  * - LGPL
5701  *
5702  * navbar
5703  * 
5704  */
5705
5706 /**
5707  * @class Roo.bootstrap.nav.Simplebar
5708  * @extends Roo.bootstrap.nav.Bar
5709  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5710  * Bootstrap Sidebar class
5711  *
5712  * @cfg {Boolean} inverse is inverted color
5713  * 
5714  * @cfg {String} type (nav | pills | tabs)
5715  * @cfg {Boolean} arrangement stacked | justified
5716  * @cfg {String} align (left | right) alignment
5717  * 
5718  * @cfg {Boolean} main (true|false) main nav bar? default false
5719  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5720  * 
5721  * @cfg {String} tag (header|footer|nav|div) default is nav 
5722
5723  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5724  * 
5725  * 
5726  * @constructor
5727  * Create a new Sidebar
5728  * @param {Object} config The config object
5729  */
5730
5731
5732 Roo.bootstrap.nav.Simplebar = function(config){
5733     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5734 };
5735
5736 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5737     
5738     inverse: false,
5739     
5740     type: false,
5741     arrangement: '',
5742     align : false,
5743     
5744     weight : 'light',
5745     
5746     main : false,
5747     
5748     
5749     tag : false,
5750     
5751     
5752     getAutoCreate : function(){
5753         
5754         
5755         var cfg = {
5756             tag : this.tag || 'div',
5757             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5758         };
5759         if (['light','white'].indexOf(this.weight) > -1) {
5760             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5761         }
5762         cfg.cls += ' bg-' + this.weight;
5763         
5764         if (this.inverse) {
5765             cfg.cls += ' navbar-inverse';
5766             
5767         }
5768         
5769         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5770         
5771         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5772             return cfg;
5773         }
5774         
5775         
5776     
5777         
5778         cfg.cn = [
5779             {
5780                 cls: 'nav nav-' + this.xtype,
5781                 tag : 'ul'
5782             }
5783         ];
5784         
5785          
5786         this.type = this.type || 'nav';
5787         if (['tabs','pills'].indexOf(this.type) != -1) {
5788             cfg.cn[0].cls += ' nav-' + this.type
5789         
5790         
5791         } else {
5792             if (this.type!=='nav') {
5793                 Roo.log('nav type must be nav/tabs/pills')
5794             }
5795             cfg.cn[0].cls += ' navbar-nav'
5796         }
5797         
5798         
5799         
5800         
5801         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5802             cfg.cn[0].cls += ' nav-' + this.arrangement;
5803         }
5804         
5805         
5806         if (this.align === 'right') {
5807             cfg.cn[0].cls += ' navbar-right';
5808         }
5809         
5810         
5811         
5812         
5813         return cfg;
5814     
5815         
5816     }
5817     
5818     
5819     
5820 });
5821
5822
5823
5824  
5825
5826  
5827        /*
5828  * - LGPL
5829  *
5830  * navbar
5831  * navbar-fixed-top
5832  * navbar-expand-md  fixed-top 
5833  */
5834
5835 /**
5836  * @class Roo.bootstrap.nav.Headerbar
5837  * @extends Roo.bootstrap.nav.Simplebar
5838  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5839  * Bootstrap Sidebar class
5840  *
5841  * @cfg {String} brand what is brand
5842  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5843  * @cfg {String} brand_href href of the brand
5844  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5845  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5846  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5847  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5848  * 
5849  * @constructor
5850  * Create a new Sidebar
5851  * @param {Object} config The config object
5852  */
5853
5854
5855 Roo.bootstrap.nav.Headerbar = function(config){
5856     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5857       
5858 };
5859
5860 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5861     
5862     position: '',
5863     brand: '',
5864     brand_href: false,
5865     srButton : true,
5866     autohide : false,
5867     desktopCenter : false,
5868    
5869     
5870     getAutoCreate : function(){
5871         
5872         var   cfg = {
5873             tag: this.nav || 'nav',
5874             cls: 'navbar navbar-expand-md',
5875             role: 'navigation',
5876             cn: []
5877         };
5878         
5879         var cn = cfg.cn;
5880         if (this.desktopCenter) {
5881             cn.push({cls : 'container', cn : []});
5882             cn = cn[0].cn;
5883         }
5884         
5885         if(this.srButton){
5886             var btn = {
5887                 tag: 'button',
5888                 type: 'button',
5889                 cls: 'navbar-toggle navbar-toggler',
5890                 'data-toggle': 'collapse',
5891                 cn: [
5892                     {
5893                         tag: 'span',
5894                         cls: 'sr-only',
5895                         html: 'Toggle navigation'
5896                     },
5897                     {
5898                         tag: 'span',
5899                         cls: 'icon-bar navbar-toggler-icon'
5900                     },
5901                     {
5902                         tag: 'span',
5903                         cls: 'icon-bar'
5904                     },
5905                     {
5906                         tag: 'span',
5907                         cls: 'icon-bar'
5908                     }
5909                 ]
5910             };
5911             
5912             cn.push( Roo.bootstrap.version == 4 ? btn : {
5913                 tag: 'div',
5914                 cls: 'navbar-header',
5915                 cn: [
5916                     btn
5917                 ]
5918             });
5919         }
5920         
5921         cn.push({
5922             tag: 'div',
5923             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5924             cn : []
5925         });
5926         
5927         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5928         
5929         if (['light','white'].indexOf(this.weight) > -1) {
5930             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5931         }
5932         cfg.cls += ' bg-' + this.weight;
5933         
5934         
5935         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5936             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5937             
5938             // tag can override this..
5939             
5940             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5941         }
5942         
5943         if (this.brand !== '') {
5944             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5945             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5946                 tag: 'a',
5947                 href: this.brand_href ? this.brand_href : '#',
5948                 cls: 'navbar-brand',
5949                 cn: [
5950                 this.brand
5951                 ]
5952             });
5953         }
5954         
5955         if(this.main){
5956             cfg.cls += ' main-nav';
5957         }
5958         
5959         
5960         return cfg;
5961
5962         
5963     },
5964     getHeaderChildContainer : function()
5965     {
5966         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5967             return this.el.select('.navbar-header',true).first();
5968         }
5969         
5970         return this.getChildContainer();
5971     },
5972     
5973     getChildContainer : function()
5974     {
5975          
5976         return this.el.select('.roo-navbar-collapse',true).first();
5977          
5978         
5979     },
5980     
5981     initEvents : function()
5982     {
5983         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5984         
5985         if (this.autohide) {
5986             
5987             var prevScroll = 0;
5988             var ft = this.el;
5989             
5990             Roo.get(document).on('scroll',function(e) {
5991                 var ns = Roo.get(document).getScroll().top;
5992                 var os = prevScroll;
5993                 prevScroll = ns;
5994                 
5995                 if(ns > os){
5996                     ft.removeClass('slideDown');
5997                     ft.addClass('slideUp');
5998                     return;
5999                 }
6000                 ft.removeClass('slideUp');
6001                 ft.addClass('slideDown');
6002                  
6003               
6004           },this);
6005         }
6006     }    
6007     
6008 });
6009
6010
6011
6012  
6013
6014  /*
6015  * - LGPL
6016  *
6017  * navbar
6018  * 
6019  */
6020
6021 /**
6022  * @class Roo.bootstrap.nav.Sidebar
6023  * @extends Roo.bootstrap.nav.Bar
6024  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6025  * Bootstrap Sidebar class
6026  * 
6027  * @constructor
6028  * Create a new Sidebar
6029  * @param {Object} config The config object
6030  */
6031
6032
6033 Roo.bootstrap.nav.Sidebar = function(config){
6034     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6035 };
6036
6037 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6038     
6039     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6040     
6041     getAutoCreate : function(){
6042         
6043         
6044         return  {
6045             tag: 'div',
6046             cls: 'sidebar sidebar-nav'
6047         };
6048     
6049         
6050     }
6051     
6052     
6053     
6054 });
6055
6056
6057
6058  
6059
6060  /*
6061  * - LGPL
6062  *
6063  * nav group
6064  * 
6065  */
6066
6067 /**
6068  * @class Roo.bootstrap.nav.Group
6069  * @extends Roo.bootstrap.Component
6070  * @children Roo.bootstrap.nav.Item
6071  * Bootstrap NavGroup class
6072  * @cfg {String} align (left|right)
6073  * @cfg {Boolean} inverse
6074  * @cfg {String} type (nav|pills|tab) default nav
6075  * @cfg {String} navId - reference Id for navbar.
6076  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6077  * 
6078  * @constructor
6079  * Create a new nav group
6080  * @param {Object} config The config object
6081  */
6082
6083 Roo.bootstrap.nav.Group = function(config){
6084     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6085     this.navItems = [];
6086    
6087     Roo.bootstrap.nav.Group.register(this);
6088      this.addEvents({
6089         /**
6090              * @event changed
6091              * Fires when the active item changes
6092              * @param {Roo.bootstrap.nav.Group} this
6093              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6094              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6095          */
6096         'changed': true
6097      });
6098     
6099 };
6100
6101 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6102     
6103     align: '',
6104     inverse: false,
6105     form: false,
6106     type: 'nav',
6107     navId : '',
6108     // private
6109     pilltype : true,
6110     
6111     navItems : false, 
6112     
6113     getAutoCreate : function()
6114     {
6115         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6116         
6117         cfg = {
6118             tag : 'ul',
6119             cls: 'nav' 
6120         };
6121         if (Roo.bootstrap.version == 4) {
6122             if (['tabs','pills'].indexOf(this.type) != -1) {
6123                 cfg.cls += ' nav-' + this.type; 
6124             } else {
6125                 // trying to remove so header bar can right align top?
6126                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6127                     // do not use on header bar... 
6128                     cfg.cls += ' navbar-nav';
6129                 }
6130             }
6131             
6132         } else {
6133             if (['tabs','pills'].indexOf(this.type) != -1) {
6134                 cfg.cls += ' nav-' + this.type
6135             } else {
6136                 if (this.type !== 'nav') {
6137                     Roo.log('nav type must be nav/tabs/pills')
6138                 }
6139                 cfg.cls += ' navbar-nav'
6140             }
6141         }
6142         
6143         if (this.parent() && this.parent().sidebar) {
6144             cfg = {
6145                 tag: 'ul',
6146                 cls: 'dashboard-menu sidebar-menu'
6147             };
6148             
6149             return cfg;
6150         }
6151         
6152         if (this.form === true) {
6153             cfg = {
6154                 tag: 'form',
6155                 cls: 'navbar-form form-inline'
6156             };
6157             //nav navbar-right ml-md-auto
6158             if (this.align === 'right') {
6159                 cfg.cls += ' navbar-right ml-md-auto';
6160             } else {
6161                 cfg.cls += ' navbar-left';
6162             }
6163         }
6164         
6165         if (this.align === 'right') {
6166             cfg.cls += ' navbar-right ml-md-auto';
6167         } else {
6168             cfg.cls += ' mr-auto';
6169         }
6170         
6171         if (this.inverse) {
6172             cfg.cls += ' navbar-inverse';
6173             
6174         }
6175         
6176         
6177         return cfg;
6178     },
6179     /**
6180     * sets the active Navigation item
6181     * @param {Roo.bootstrap.nav.Item} the new current navitem
6182     */
6183     setActiveItem : function(item)
6184     {
6185         var prev = false;
6186         Roo.each(this.navItems, function(v){
6187             if (v == item) {
6188                 return ;
6189             }
6190             if (v.isActive()) {
6191                 v.setActive(false, true);
6192                 prev = v;
6193                 
6194             }
6195             
6196         });
6197
6198         item.setActive(true, true);
6199         this.fireEvent('changed', this, item, prev);
6200         
6201         
6202     },
6203     /**
6204     * gets the active Navigation item
6205     * @return {Roo.bootstrap.nav.Item} the current navitem
6206     */
6207     getActive : function()
6208     {
6209         
6210         var prev = false;
6211         Roo.each(this.navItems, function(v){
6212             
6213             if (v.isActive()) {
6214                 prev = v;
6215                 
6216             }
6217             
6218         });
6219         return prev;
6220     },
6221     
6222     indexOfNav : function()
6223     {
6224         
6225         var prev = false;
6226         Roo.each(this.navItems, function(v,i){
6227             
6228             if (v.isActive()) {
6229                 prev = i;
6230                 
6231             }
6232             
6233         });
6234         return prev;
6235     },
6236     /**
6237     * adds a Navigation item
6238     * @param {Roo.bootstrap.nav.Item} the navitem to add
6239     */
6240     addItem : function(cfg)
6241     {
6242         if (this.form && Roo.bootstrap.version == 4) {
6243             cfg.tag = 'div';
6244         }
6245         var cn = new Roo.bootstrap.nav.Item(cfg);
6246         this.register(cn);
6247         cn.parentId = this.id;
6248         cn.onRender(this.el, null);
6249         return cn;
6250     },
6251     /**
6252     * register a Navigation item
6253     * @param {Roo.bootstrap.nav.Item} the navitem to add
6254     */
6255     register : function(item)
6256     {
6257         this.navItems.push( item);
6258         item.navId = this.navId;
6259     
6260     },
6261     
6262     /**
6263     * clear all the Navigation item
6264     */
6265    
6266     clearAll : function()
6267     {
6268         this.navItems = [];
6269         this.el.dom.innerHTML = '';
6270     },
6271     
6272     getNavItem: function(tabId)
6273     {
6274         var ret = false;
6275         Roo.each(this.navItems, function(e) {
6276             if (e.tabId == tabId) {
6277                ret =  e;
6278                return false;
6279             }
6280             return true;
6281             
6282         });
6283         return ret;
6284     },
6285     
6286     setActiveNext : function()
6287     {
6288         var i = this.indexOfNav(this.getActive());
6289         if (i > this.navItems.length) {
6290             return;
6291         }
6292         this.setActiveItem(this.navItems[i+1]);
6293     },
6294     setActivePrev : function()
6295     {
6296         var i = this.indexOfNav(this.getActive());
6297         if (i  < 1) {
6298             return;
6299         }
6300         this.setActiveItem(this.navItems[i-1]);
6301     },
6302     clearWasActive : function(except) {
6303         Roo.each(this.navItems, function(e) {
6304             if (e.tabId != except.tabId && e.was_active) {
6305                e.was_active = false;
6306                return false;
6307             }
6308             return true;
6309             
6310         });
6311     },
6312     getWasActive : function ()
6313     {
6314         var r = false;
6315         Roo.each(this.navItems, function(e) {
6316             if (e.was_active) {
6317                r = e;
6318                return false;
6319             }
6320             return true;
6321             
6322         });
6323         return r;
6324     }
6325     
6326     
6327 });
6328
6329  
6330 Roo.apply(Roo.bootstrap.nav.Group, {
6331     
6332     groups: {},
6333      /**
6334     * register a Navigation Group
6335     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6336     */
6337     register : function(navgrp)
6338     {
6339         this.groups[navgrp.navId] = navgrp;
6340         
6341     },
6342     /**
6343     * fetch a Navigation Group based on the navigation ID
6344     * @param {string} the navgroup to add
6345     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6346     */
6347     get: function(navId) {
6348         if (typeof(this.groups[navId]) == 'undefined') {
6349             return false;
6350             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6351         }
6352         return this.groups[navId] ;
6353     }
6354     
6355     
6356     
6357 });
6358
6359  /**
6360  * @class Roo.bootstrap.nav.Item
6361  * @extends Roo.bootstrap.Component
6362  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6363  * @parent Roo.bootstrap.nav.Group
6364  * @licence LGPL
6365  * Bootstrap Navbar.NavItem class
6366  * 
6367  * @cfg {String} href  link to
6368  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6369  * @cfg {Boolean} button_outline show and outlined button
6370  * @cfg {String} html content of button
6371  * @cfg {String} badge text inside badge
6372  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6373  * @cfg {String} glyphicon DEPRICATED - use fa
6374  * @cfg {String} icon DEPRICATED - use fa
6375  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6376  * @cfg {Boolean} active Is item active
6377  * @cfg {Boolean} disabled Is item disabled
6378  * @cfg {String} linkcls  Link Class
6379  * @cfg {Boolean} preventDefault (true | false) default false
6380  * @cfg {String} tabId the tab that this item activates.
6381  * @cfg {String} tagtype (a|span) render as a href or span?
6382  * @cfg {Boolean} animateRef (true|false) link to element default false  
6383  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6384   
6385  * @constructor
6386  * Create a new Navbar Item
6387  * @param {Object} config The config object
6388  */
6389 Roo.bootstrap.nav.Item = function(config){
6390     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6391     this.addEvents({
6392         // raw events
6393         /**
6394          * @event click
6395          * The raw click event for the entire grid.
6396          * @param {Roo.EventObject} e
6397          */
6398         "click" : true,
6399          /**
6400             * @event changed
6401             * Fires when the active item active state changes
6402             * @param {Roo.bootstrap.nav.Item} this
6403             * @param {boolean} state the new state
6404              
6405          */
6406         'changed': true,
6407         /**
6408             * @event scrollto
6409             * Fires when scroll to element
6410             * @param {Roo.bootstrap.nav.Item} this
6411             * @param {Object} options
6412             * @param {Roo.EventObject} e
6413              
6414          */
6415         'scrollto': true
6416     });
6417    
6418 };
6419
6420 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6421     
6422     href: false,
6423     html: '',
6424     badge: '',
6425     icon: false,
6426     fa : false,
6427     glyphicon: false,
6428     active: false,
6429     preventDefault : false,
6430     tabId : false,
6431     tagtype : 'a',
6432     tag: 'li',
6433     disabled : false,
6434     animateRef : false,
6435     was_active : false,
6436     button_weight : '',
6437     button_outline : false,
6438     linkcls : '',
6439     navLink: false,
6440     
6441     getAutoCreate : function(){
6442          
6443         var cfg = {
6444             tag: this.tag,
6445             cls: 'nav-item'
6446         };
6447         
6448         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6449         
6450         if (this.active) {
6451             cfg.cls +=  ' active' ;
6452         }
6453         if (this.disabled) {
6454             cfg.cls += ' disabled';
6455         }
6456         
6457         // BS4 only?
6458         if (this.button_weight.length) {
6459             cfg.tag = this.href ? 'a' : 'button';
6460             cfg.html = this.html || '';
6461             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6462             if (this.href) {
6463                 cfg.href = this.href;
6464             }
6465             if (this.fa) {
6466                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6467             } else {
6468                 cfg.cls += " nav-html";
6469             }
6470             
6471             // menu .. should add dropdown-menu class - so no need for carat..
6472             
6473             if (this.badge !== '') {
6474                  
6475                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6476             }
6477             return cfg;
6478         }
6479         
6480         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6481             cfg.cn = [
6482                 {
6483                     tag: this.tagtype,
6484                     href : this.href || "#",
6485                     html: this.html || '',
6486                     cls : ''
6487                 }
6488             ];
6489             if (this.tagtype == 'a') {
6490                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6491         
6492             }
6493             if (this.icon) {
6494                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6495             } else  if (this.fa) {
6496                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6497             } else if(this.glyphicon) {
6498                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6499             } else {
6500                 cfg.cn[0].cls += " nav-html";
6501             }
6502             
6503             if (this.menu) {
6504                 cfg.cn[0].html += " <span class='caret'></span>";
6505              
6506             }
6507             
6508             if (this.badge !== '') {
6509                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6510             }
6511         }
6512         
6513         
6514         
6515         return cfg;
6516     },
6517     onRender : function(ct, position)
6518     {
6519        // Roo.log("Call onRender: " + this.xtype);
6520         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6521             this.tag = 'div';
6522         }
6523         
6524         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6525         this.navLink = this.el.select('.nav-link',true).first();
6526         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6527         return ret;
6528     },
6529       
6530     
6531     initEvents: function() 
6532     {
6533         if (typeof (this.menu) != 'undefined') {
6534             this.menu.parentType = this.xtype;
6535             this.menu.triggerEl = this.el;
6536             this.menu = this.addxtype(Roo.apply({}, this.menu));
6537         }
6538         
6539         this.el.on('click', this.onClick, this);
6540         
6541         //if(this.tagtype == 'span'){
6542         //    this.el.select('span',true).on('click', this.onClick, this);
6543         //}
6544        
6545         // at this point parent should be available..
6546         this.parent().register(this);
6547     },
6548     
6549     onClick : function(e)
6550     {
6551         if (e.getTarget('.dropdown-menu-item')) {
6552             // did you click on a menu itemm.... - then don't trigger onclick..
6553             return;
6554         }
6555         
6556         if(
6557                 this.preventDefault ||
6558                                 this.href === false ||
6559                 this.href === '#' 
6560         ){
6561             //Roo.log("NavItem - prevent Default?");
6562             e.preventDefault();
6563         }
6564         
6565         if (this.disabled) {
6566             return;
6567         }
6568         
6569         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6570         if (tg && tg.transition) {
6571             Roo.log("waiting for the transitionend");
6572             return;
6573         }
6574         
6575         
6576         
6577         //Roo.log("fire event clicked");
6578         if(this.fireEvent('click', this, e) === false){
6579             return;
6580         };
6581         
6582         if(this.tagtype == 'span'){
6583             return;
6584         }
6585         
6586         //Roo.log(this.href);
6587         var ael = this.el.select('a',true).first();
6588         //Roo.log(ael);
6589         
6590         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6591             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6592             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6593                 return; // ignore... - it's a 'hash' to another page.
6594             }
6595             Roo.log("NavItem - prevent Default?");
6596             e.preventDefault();
6597             this.scrollToElement(e);
6598         }
6599         
6600         
6601         var p =  this.parent();
6602    
6603         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6604             if (typeof(p.setActiveItem) !== 'undefined') {
6605                 p.setActiveItem(this);
6606             }
6607         }
6608         
6609         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6610         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6611             // remove the collapsed menu expand...
6612             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6613         }
6614     },
6615     
6616     isActive: function () {
6617         return this.active
6618     },
6619     setActive : function(state, fire, is_was_active)
6620     {
6621         if (this.active && !state && this.navId) {
6622             this.was_active = true;
6623             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6624             if (nv) {
6625                 nv.clearWasActive(this);
6626             }
6627             
6628         }
6629         this.active = state;
6630         
6631         if (!state ) {
6632             this.el.removeClass('active');
6633             this.navLink ? this.navLink.removeClass('active') : false;
6634         } else if (!this.el.hasClass('active')) {
6635             
6636             this.el.addClass('active');
6637             if (Roo.bootstrap.version == 4 && this.navLink ) {
6638                 this.navLink.addClass('active');
6639             }
6640             
6641         }
6642         if (fire) {
6643             this.fireEvent('changed', this, state);
6644         }
6645         
6646         // show a panel if it's registered and related..
6647         
6648         if (!this.navId || !this.tabId || !state || is_was_active) {
6649             return;
6650         }
6651         
6652         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6653         if (!tg) {
6654             return;
6655         }
6656         var pan = tg.getPanelByName(this.tabId);
6657         if (!pan) {
6658             return;
6659         }
6660         // if we can not flip to new panel - go back to old nav highlight..
6661         if (false == tg.showPanel(pan)) {
6662             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6663             if (nv) {
6664                 var onav = nv.getWasActive();
6665                 if (onav) {
6666                     onav.setActive(true, false, true);
6667                 }
6668             }
6669             
6670         }
6671         
6672         
6673         
6674     },
6675      // this should not be here...
6676     setDisabled : function(state)
6677     {
6678         this.disabled = state;
6679         if (!state ) {
6680             this.el.removeClass('disabled');
6681         } else if (!this.el.hasClass('disabled')) {
6682             this.el.addClass('disabled');
6683         }
6684         
6685     },
6686     
6687     /**
6688      * Fetch the element to display the tooltip on.
6689      * @return {Roo.Element} defaults to this.el
6690      */
6691     tooltipEl : function()
6692     {
6693         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6694     },
6695     
6696     scrollToElement : function(e)
6697     {
6698         var c = document.body;
6699         
6700         /*
6701          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6702          */
6703         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6704             c = document.documentElement;
6705         }
6706         
6707         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6708         
6709         if(!target){
6710             return;
6711         }
6712
6713         var o = target.calcOffsetsTo(c);
6714         
6715         var options = {
6716             target : target,
6717             value : o[1]
6718         };
6719         
6720         this.fireEvent('scrollto', this, options, e);
6721         
6722         Roo.get(c).scrollTo('top', options.value, true);
6723         
6724         return;
6725     },
6726     /**
6727      * Set the HTML (text content) of the item
6728      * @param {string} html  content for the nav item
6729      */
6730     setHtml : function(html)
6731     {
6732         this.html = html;
6733         this.htmlEl.dom.innerHTML = html;
6734         
6735     } 
6736 });
6737  
6738
6739  /*
6740  * - LGPL
6741  *
6742  * sidebar item
6743  *
6744  *  li
6745  *    <span> icon </span>
6746  *    <span> text </span>
6747  *    <span>badge </span>
6748  */
6749
6750 /**
6751  * @class Roo.bootstrap.nav.SidebarItem
6752  * @extends Roo.bootstrap.nav.Item
6753  * Bootstrap Navbar.NavSidebarItem class
6754  * 
6755  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6756  * {Boolean} open is the menu open
6757  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6758  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6759  * {String} buttonSize (sm|md|lg)the extra classes for the button
6760  * {Boolean} showArrow show arrow next to the text (default true)
6761  * @constructor
6762  * Create a new Navbar Button
6763  * @param {Object} config The config object
6764  */
6765 Roo.bootstrap.nav.SidebarItem = function(config){
6766     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6767     this.addEvents({
6768         // raw events
6769         /**
6770          * @event click
6771          * The raw click event for the entire grid.
6772          * @param {Roo.EventObject} e
6773          */
6774         "click" : true,
6775          /**
6776             * @event changed
6777             * Fires when the active item active state changes
6778             * @param {Roo.bootstrap.nav.SidebarItem} this
6779             * @param {boolean} state the new state
6780              
6781          */
6782         'changed': true
6783     });
6784    
6785 };
6786
6787 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6788     
6789     badgeWeight : 'default',
6790     
6791     open: false,
6792     
6793     buttonView : false,
6794     
6795     buttonWeight : 'default',
6796     
6797     buttonSize : 'md',
6798     
6799     showArrow : true,
6800     
6801     getAutoCreate : function(){
6802         
6803         
6804         var a = {
6805                 tag: 'a',
6806                 href : this.href || '#',
6807                 cls: '',
6808                 html : '',
6809                 cn : []
6810         };
6811         
6812         if(this.buttonView){
6813             a = {
6814                 tag: 'button',
6815                 href : this.href || '#',
6816                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6817                 html : this.html,
6818                 cn : []
6819             };
6820         }
6821         
6822         var cfg = {
6823             tag: 'li',
6824             cls: '',
6825             cn: [ a ]
6826         };
6827         
6828         if (this.active) {
6829             cfg.cls += ' active';
6830         }
6831         
6832         if (this.disabled) {
6833             cfg.cls += ' disabled';
6834         }
6835         if (this.open) {
6836             cfg.cls += ' open x-open';
6837         }
6838         // left icon..
6839         if (this.glyphicon || this.icon) {
6840             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6841             a.cn.push({ tag : 'i', cls : c }) ;
6842         }
6843         
6844         if(!this.buttonView){
6845             var span = {
6846                 tag: 'span',
6847                 html : this.html || ''
6848             };
6849
6850             a.cn.push(span);
6851             
6852         }
6853         
6854         if (this.badge !== '') {
6855             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6856         }
6857         
6858         if (this.menu) {
6859             
6860             if(this.showArrow){
6861                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6862             }
6863             
6864             a.cls += ' dropdown-toggle treeview' ;
6865         }
6866         
6867         return cfg;
6868     },
6869     
6870     initEvents : function()
6871     { 
6872         if (typeof (this.menu) != 'undefined') {
6873             this.menu.parentType = this.xtype;
6874             this.menu.triggerEl = this.el;
6875             this.menu = this.addxtype(Roo.apply({}, this.menu));
6876         }
6877         
6878         this.el.on('click', this.onClick, this);
6879         
6880         if(this.badge !== ''){
6881             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6882         }
6883         
6884     },
6885     
6886     onClick : function(e)
6887     {
6888         if(this.disabled){
6889             e.preventDefault();
6890             return;
6891         }
6892         
6893         if(this.preventDefault){
6894             e.preventDefault();
6895         }
6896         
6897         this.fireEvent('click', this, e);
6898     },
6899     
6900     disable : function()
6901     {
6902         this.setDisabled(true);
6903     },
6904     
6905     enable : function()
6906     {
6907         this.setDisabled(false);
6908     },
6909     
6910     setDisabled : function(state)
6911     {
6912         if(this.disabled == state){
6913             return;
6914         }
6915         
6916         this.disabled = state;
6917         
6918         if (state) {
6919             this.el.addClass('disabled');
6920             return;
6921         }
6922         
6923         this.el.removeClass('disabled');
6924         
6925         return;
6926     },
6927     
6928     setActive : function(state)
6929     {
6930         if(this.active == state){
6931             return;
6932         }
6933         
6934         this.active = state;
6935         
6936         if (state) {
6937             this.el.addClass('active');
6938             return;
6939         }
6940         
6941         this.el.removeClass('active');
6942         
6943         return;
6944     },
6945     
6946     isActive: function () 
6947     {
6948         return this.active;
6949     },
6950     
6951     setBadge : function(str)
6952     {
6953         if(!this.badgeEl){
6954             return;
6955         }
6956         
6957         this.badgeEl.dom.innerHTML = str;
6958     }
6959     
6960    
6961      
6962  
6963 });
6964  
6965
6966  /*
6967  * - LGPL
6968  *
6969  * nav progress bar
6970  * 
6971  */
6972
6973 /**
6974  * @class Roo.bootstrap.nav.ProgressBar
6975  * @extends Roo.bootstrap.Component
6976  * @children Roo.bootstrap.nav.ProgressBarItem
6977  * Bootstrap NavProgressBar class
6978  * 
6979  * @constructor
6980  * Create a new nav progress bar - a bar indicating step along a process
6981  * @param {Object} config The config object
6982  */
6983
6984 Roo.bootstrap.nav.ProgressBar = function(config){
6985     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6986
6987     this.bullets = this.bullets || [];
6988    
6989 //    Roo.bootstrap.nav.ProgressBar.register(this);
6990      this.addEvents({
6991         /**
6992              * @event changed
6993              * Fires when the active item changes
6994              * @param {Roo.bootstrap.nav.ProgressBar} this
6995              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6996              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
6997          */
6998         'changed': true
6999      });
7000     
7001 };
7002
7003 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
7004     /**
7005      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7006      * Bullets for the Nav Progress bar for the toolbar
7007      */
7008     bullets : [],
7009     barItems : [],
7010     
7011     getAutoCreate : function()
7012     {
7013         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7014         
7015         cfg = {
7016             tag : 'div',
7017             cls : 'roo-navigation-bar-group',
7018             cn : [
7019                 {
7020                     tag : 'div',
7021                     cls : 'roo-navigation-top-bar'
7022                 },
7023                 {
7024                     tag : 'div',
7025                     cls : 'roo-navigation-bullets-bar',
7026                     cn : [
7027                         {
7028                             tag : 'ul',
7029                             cls : 'roo-navigation-bar'
7030                         }
7031                     ]
7032                 },
7033                 
7034                 {
7035                     tag : 'div',
7036                     cls : 'roo-navigation-bottom-bar'
7037                 }
7038             ]
7039             
7040         };
7041         
7042         return cfg;
7043         
7044     },
7045     
7046     initEvents: function() 
7047     {
7048         
7049     },
7050     
7051     onRender : function(ct, position) 
7052     {
7053         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7054         
7055         if(this.bullets.length){
7056             Roo.each(this.bullets, function(b){
7057                this.addItem(b);
7058             }, this);
7059         }
7060         
7061         this.format();
7062         
7063     },
7064     
7065     addItem : function(cfg)
7066     {
7067         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7068         
7069         item.parentId = this.id;
7070         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7071         
7072         if(cfg.html){
7073             var top = new Roo.bootstrap.Element({
7074                 tag : 'div',
7075                 cls : 'roo-navigation-bar-text'
7076             });
7077             
7078             var bottom = new Roo.bootstrap.Element({
7079                 tag : 'div',
7080                 cls : 'roo-navigation-bar-text'
7081             });
7082             
7083             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7084             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7085             
7086             var topText = new Roo.bootstrap.Element({
7087                 tag : 'span',
7088                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7089             });
7090             
7091             var bottomText = new Roo.bootstrap.Element({
7092                 tag : 'span',
7093                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7094             });
7095             
7096             topText.onRender(top.el, null);
7097             bottomText.onRender(bottom.el, null);
7098             
7099             item.topEl = top;
7100             item.bottomEl = bottom;
7101         }
7102         
7103         this.barItems.push(item);
7104         
7105         return item;
7106     },
7107     
7108     getActive : function()
7109     {
7110         var active = false;
7111         
7112         Roo.each(this.barItems, function(v){
7113             
7114             if (!v.isActive()) {
7115                 return;
7116             }
7117             
7118             active = v;
7119             return false;
7120             
7121         });
7122         
7123         return active;
7124     },
7125     
7126     setActiveItem : function(item)
7127     {
7128         var prev = false;
7129         
7130         Roo.each(this.barItems, function(v){
7131             if (v.rid == item.rid) {
7132                 return ;
7133             }
7134             
7135             if (v.isActive()) {
7136                 v.setActive(false);
7137                 prev = v;
7138             }
7139         });
7140
7141         item.setActive(true);
7142         
7143         this.fireEvent('changed', this, item, prev);
7144     },
7145     
7146     getBarItem: function(rid)
7147     {
7148         var ret = false;
7149         
7150         Roo.each(this.barItems, function(e) {
7151             if (e.rid != rid) {
7152                 return;
7153             }
7154             
7155             ret =  e;
7156             return false;
7157         });
7158         
7159         return ret;
7160     },
7161     
7162     indexOfItem : function(item)
7163     {
7164         var index = false;
7165         
7166         Roo.each(this.barItems, function(v, i){
7167             
7168             if (v.rid != item.rid) {
7169                 return;
7170             }
7171             
7172             index = i;
7173             return false
7174         });
7175         
7176         return index;
7177     },
7178     
7179     setActiveNext : function()
7180     {
7181         var i = this.indexOfItem(this.getActive());
7182         
7183         if (i > this.barItems.length) {
7184             return;
7185         }
7186         
7187         this.setActiveItem(this.barItems[i+1]);
7188     },
7189     
7190     setActivePrev : function()
7191     {
7192         var i = this.indexOfItem(this.getActive());
7193         
7194         if (i  < 1) {
7195             return;
7196         }
7197         
7198         this.setActiveItem(this.barItems[i-1]);
7199     },
7200     
7201     format : function()
7202     {
7203         if(!this.barItems.length){
7204             return;
7205         }
7206      
7207         var width = 100 / this.barItems.length;
7208         
7209         Roo.each(this.barItems, function(i){
7210             i.el.setStyle('width', width + '%');
7211             i.topEl.el.setStyle('width', width + '%');
7212             i.bottomEl.el.setStyle('width', width + '%');
7213         }, this);
7214         
7215     }
7216     
7217 });
7218 /*
7219  * - LGPL
7220  *
7221  * Nav Progress Item
7222  * 
7223  */
7224
7225 /**
7226  * @class Roo.bootstrap.nav.ProgressBarItem
7227  * @extends Roo.bootstrap.Component
7228  * Bootstrap NavProgressBarItem class
7229  * @cfg {String} rid the reference id
7230  * @cfg {Boolean} active (true|false) Is item active default false
7231  * @cfg {Boolean} disabled (true|false) Is item active default false
7232  * @cfg {String} html
7233  * @cfg {String} position (top|bottom) text position default bottom
7234  * @cfg {String} icon show icon instead of number
7235  * 
7236  * @constructor
7237  * Create a new NavProgressBarItem
7238  * @param {Object} config The config object
7239  */
7240 Roo.bootstrap.nav.ProgressBarItem = function(config){
7241     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7242     this.addEvents({
7243         // raw events
7244         /**
7245          * @event click
7246          * The raw click event for the entire grid.
7247          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7248          * @param {Roo.EventObject} e
7249          */
7250         "click" : true
7251     });
7252    
7253 };
7254
7255 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7256     
7257     rid : '',
7258     active : false,
7259     disabled : false,
7260     html : '',
7261     position : 'bottom',
7262     icon : false,
7263     
7264     getAutoCreate : function()
7265     {
7266         var iconCls = 'roo-navigation-bar-item-icon';
7267         
7268         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7269         
7270         var cfg = {
7271             tag: 'li',
7272             cls: 'roo-navigation-bar-item',
7273             cn : [
7274                 {
7275                     tag : 'i',
7276                     cls : iconCls
7277                 }
7278             ]
7279         };
7280         
7281         if(this.active){
7282             cfg.cls += ' active';
7283         }
7284         if(this.disabled){
7285             cfg.cls += ' disabled';
7286         }
7287         
7288         return cfg;
7289     },
7290     
7291     disable : function()
7292     {
7293         this.setDisabled(true);
7294     },
7295     
7296     enable : function()
7297     {
7298         this.setDisabled(false);
7299     },
7300     
7301     initEvents: function() 
7302     {
7303         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7304         
7305         this.iconEl.on('click', this.onClick, this);
7306     },
7307     
7308     onClick : function(e)
7309     {
7310         e.preventDefault();
7311         
7312         if(this.disabled){
7313             return;
7314         }
7315         
7316         if(this.fireEvent('click', this, e) === false){
7317             return;
7318         };
7319         
7320         this.parent().setActiveItem(this);
7321     },
7322     
7323     isActive: function () 
7324     {
7325         return this.active;
7326     },
7327     
7328     setActive : function(state)
7329     {
7330         if(this.active == state){
7331             return;
7332         }
7333         
7334         this.active = state;
7335         
7336         if (state) {
7337             this.el.addClass('active');
7338             return;
7339         }
7340         
7341         this.el.removeClass('active');
7342         
7343         return;
7344     },
7345     
7346     setDisabled : function(state)
7347     {
7348         if(this.disabled == state){
7349             return;
7350         }
7351         
7352         this.disabled = state;
7353         
7354         if (state) {
7355             this.el.addClass('disabled');
7356             return;
7357         }
7358         
7359         this.el.removeClass('disabled');
7360     },
7361     
7362     tooltipEl : function()
7363     {
7364         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7365     }
7366 });
7367  
7368
7369  /*
7370  * - LGPL
7371  *
7372  *  Breadcrumb Nav
7373  * 
7374  */
7375 Roo.namespace('Roo.bootstrap.breadcrumb');
7376
7377
7378 /**
7379  * @class Roo.bootstrap.breadcrumb.Nav
7380  * @extends Roo.bootstrap.Component
7381  * Bootstrap Breadcrumb Nav Class
7382  *  
7383  * @children Roo.bootstrap.breadcrumb.Item
7384  * 
7385  * @constructor
7386  * Create a new breadcrumb.Nav
7387  * @param {Object} config The config object
7388  */
7389
7390
7391 Roo.bootstrap.breadcrumb.Nav = function(config){
7392     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7393     
7394     
7395 };
7396
7397 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7398     
7399     getAutoCreate : function()
7400     {
7401
7402         var cfg = {
7403             tag: 'nav',
7404             cn : [
7405                 {
7406                     tag : 'ol',
7407                     cls : 'breadcrumb'
7408                 }
7409             ]
7410             
7411         };
7412           
7413         return cfg;
7414     },
7415     
7416     initEvents: function()
7417     {
7418         this.olEl = this.el.select('ol',true).first();    
7419     },
7420     getChildContainer : function()
7421     {
7422         return this.olEl;  
7423     }
7424     
7425 });
7426
7427  /*
7428  * - LGPL
7429  *
7430  *  Breadcrumb Item
7431  * 
7432  */
7433
7434
7435 /**
7436  * @class Roo.bootstrap.breadcrumb.Nav
7437  * @extends Roo.bootstrap.Component
7438  * @children Roo.bootstrap.Component
7439  * @parent Roo.bootstrap.breadcrumb.Nav
7440  * Bootstrap Breadcrumb Nav Class
7441  *  
7442  * 
7443  * @cfg {String} html the content of the link.
7444  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7445  * @cfg {Boolean} active is it active
7446
7447  * 
7448  * @constructor
7449  * Create a new breadcrumb.Nav
7450  * @param {Object} config The config object
7451  */
7452
7453 Roo.bootstrap.breadcrumb.Item = function(config){
7454     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7455     this.addEvents({
7456         // img events
7457         /**
7458          * @event click
7459          * The img click event for the img.
7460          * @param {Roo.EventObject} e
7461          */
7462         "click" : true
7463     });
7464     
7465 };
7466
7467 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7468     
7469     href: false,
7470     html : '',
7471     
7472     getAutoCreate : function()
7473     {
7474
7475         var cfg = {
7476             tag: 'li',
7477             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7478         };
7479         if (this.href !== false) {
7480             cfg.cn = [{
7481                 tag : 'a',
7482                 href : this.href,
7483                 html : this.html
7484             }];
7485         } else {
7486             cfg.html = this.html;
7487         }
7488         
7489         return cfg;
7490     },
7491     
7492     initEvents: function()
7493     {
7494         if (this.href) {
7495             this.el.select('a', true).first().on('click',this.onClick, this)
7496         }
7497         
7498     },
7499     onClick : function(e)
7500     {
7501         e.preventDefault();
7502         this.fireEvent('click',this,  e);
7503     }
7504     
7505 });
7506
7507  /*
7508  * - LGPL
7509  *
7510  * row
7511  * 
7512  */
7513
7514 /**
7515  * @class Roo.bootstrap.Row
7516  * @extends Roo.bootstrap.Component
7517  * @children Roo.bootstrap.Component
7518  * Bootstrap Row class (contains columns...)
7519  * 
7520  * @constructor
7521  * Create a new Row
7522  * @param {Object} config The config object
7523  */
7524
7525 Roo.bootstrap.Row = function(config){
7526     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7527 };
7528
7529 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7530     
7531     getAutoCreate : function(){
7532        return {
7533             cls: 'row clearfix'
7534        };
7535     }
7536     
7537     
7538 });
7539
7540  
7541
7542  /*
7543  * - LGPL
7544  *
7545  * pagination
7546  * 
7547  */
7548
7549 /**
7550  * @class Roo.bootstrap.Pagination
7551  * @extends Roo.bootstrap.Component
7552  * @children Roo.bootstrap.Pagination
7553  * Bootstrap Pagination class
7554  * 
7555  * @cfg {String} size (xs|sm|md|lg|xl)
7556  * @cfg {Boolean} inverse 
7557  * 
7558  * @constructor
7559  * Create a new Pagination
7560  * @param {Object} config The config object
7561  */
7562
7563 Roo.bootstrap.Pagination = function(config){
7564     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7565 };
7566
7567 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7568     
7569     cls: false,
7570     size: false,
7571     inverse: false,
7572     
7573     getAutoCreate : function(){
7574         var cfg = {
7575             tag: 'ul',
7576                 cls: 'pagination'
7577         };
7578         if (this.inverse) {
7579             cfg.cls += ' inverse';
7580         }
7581         if (this.html) {
7582             cfg.html=this.html;
7583         }
7584         if (this.cls) {
7585             cfg.cls += " " + this.cls;
7586         }
7587         return cfg;
7588     }
7589    
7590 });
7591
7592  
7593
7594  /*
7595  * - LGPL
7596  *
7597  * Pagination item
7598  * 
7599  */
7600
7601
7602 /**
7603  * @class Roo.bootstrap.PaginationItem
7604  * @extends Roo.bootstrap.Component
7605  * Bootstrap PaginationItem class
7606  * @cfg {String} html text
7607  * @cfg {String} href the link
7608  * @cfg {Boolean} preventDefault (true | false) default true
7609  * @cfg {Boolean} active (true | false) default false
7610  * @cfg {Boolean} disabled default false
7611  * 
7612  * 
7613  * @constructor
7614  * Create a new PaginationItem
7615  * @param {Object} config The config object
7616  */
7617
7618
7619 Roo.bootstrap.PaginationItem = function(config){
7620     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7621     this.addEvents({
7622         // raw events
7623         /**
7624          * @event click
7625          * The raw click event for the entire grid.
7626          * @param {Roo.EventObject} e
7627          */
7628         "click" : true
7629     });
7630 };
7631
7632 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7633     
7634     href : false,
7635     html : false,
7636     preventDefault: true,
7637     active : false,
7638     cls : false,
7639     disabled: false,
7640     
7641     getAutoCreate : function(){
7642         var cfg= {
7643             tag: 'li',
7644             cn: [
7645                 {
7646                     tag : 'a',
7647                     href : this.href ? this.href : '#',
7648                     html : this.html ? this.html : ''
7649                 }
7650             ]
7651         };
7652         
7653         if(this.cls){
7654             cfg.cls = this.cls;
7655         }
7656         
7657         if(this.disabled){
7658             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7659         }
7660         
7661         if(this.active){
7662             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7663         }
7664         
7665         return cfg;
7666     },
7667     
7668     initEvents: function() {
7669         
7670         this.el.on('click', this.onClick, this);
7671         
7672     },
7673     onClick : function(e)
7674     {
7675         Roo.log('PaginationItem on click ');
7676         if(this.preventDefault){
7677             e.preventDefault();
7678         }
7679         
7680         if(this.disabled){
7681             return;
7682         }
7683         
7684         this.fireEvent('click', this, e);
7685     }
7686    
7687 });
7688
7689  
7690
7691  /*
7692  * - LGPL
7693  *
7694  * slider
7695  * 
7696  */
7697
7698
7699 /**
7700  * @class Roo.bootstrap.Slider
7701  * @extends Roo.bootstrap.Component
7702  * Bootstrap Slider class
7703  *    
7704  * @constructor
7705  * Create a new Slider
7706  * @param {Object} config The config object
7707  */
7708
7709 Roo.bootstrap.Slider = function(config){
7710     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7711 };
7712
7713 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7714     
7715     getAutoCreate : function(){
7716         
7717         var cfg = {
7718             tag: 'div',
7719             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7720             cn: [
7721                 {
7722                     tag: 'a',
7723                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7724                 }
7725             ]
7726         };
7727         
7728         return cfg;
7729     }
7730    
7731 });
7732
7733  /*
7734  * Based on:
7735  * Ext JS Library 1.1.1
7736  * Copyright(c) 2006-2007, Ext JS, LLC.
7737  *
7738  * Originally Released Under LGPL - original licence link has changed is not relivant.
7739  *
7740  * Fork - LGPL
7741  * <script type="text/javascript">
7742  */
7743  /**
7744  * @extends Roo.dd.DDProxy
7745  * @class Roo.grid.SplitDragZone
7746  * Support for Column Header resizing
7747  * @constructor
7748  * @param {Object} config
7749  */
7750 // private
7751 // This is a support class used internally by the Grid components
7752 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7753     this.grid = grid;
7754     this.view = grid.getView();
7755     this.proxy = this.view.resizeProxy;
7756     Roo.grid.SplitDragZone.superclass.constructor.call(
7757         this,
7758         hd, // ID
7759         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7760         {  // CONFIG
7761             dragElId : Roo.id(this.proxy.dom),
7762             resizeFrame:false
7763         }
7764     );
7765     
7766     this.setHandleElId(Roo.id(hd));
7767     if (hd2 !== false) {
7768         this.setOuterHandleElId(Roo.id(hd2));
7769     }
7770     
7771     this.scroll = false;
7772 };
7773 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7774     fly: Roo.Element.fly,
7775
7776     b4StartDrag : function(x, y){
7777         this.view.headersDisabled = true;
7778         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7779                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7780         );
7781         this.proxy.setHeight(h);
7782         
7783         // for old system colWidth really stored the actual width?
7784         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7785         // which in reality did not work.. - it worked only for fixed sizes
7786         // for resizable we need to use actual sizes.
7787         var w = this.cm.getColumnWidth(this.cellIndex);
7788         if (!this.view.mainWrap) {
7789             // bootstrap.
7790             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7791         }
7792         
7793         
7794         
7795         // this was w-this.grid.minColumnWidth;
7796         // doesnt really make sense? - w = thie curren width or the rendered one?
7797         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7798         this.resetConstraints();
7799         this.setXConstraint(minw, 1000);
7800         this.setYConstraint(0, 0);
7801         this.minX = x - minw;
7802         this.maxX = x + 1000;
7803         this.startPos = x;
7804         if (!this.view.mainWrap) { // this is Bootstrap code..
7805             this.getDragEl().style.display='block';
7806         }
7807         
7808         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7809     },
7810
7811
7812     handleMouseDown : function(e){
7813         ev = Roo.EventObject.setEvent(e);
7814         var t = this.fly(ev.getTarget());
7815         if(t.hasClass("x-grid-split")){
7816             this.cellIndex = this.view.getCellIndex(t.dom);
7817             this.split = t.dom;
7818             this.cm = this.grid.colModel;
7819             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7820                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7821             }
7822         }
7823     },
7824
7825     endDrag : function(e){
7826         this.view.headersDisabled = false;
7827         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7828         var diff = endX - this.startPos;
7829         // 
7830         var w = this.cm.getColumnWidth(this.cellIndex);
7831         if (!this.view.mainWrap) {
7832             w = 0;
7833         }
7834         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7835     },
7836
7837     autoOffset : function(){
7838         this.setDelta(0,0);
7839     }
7840 });/*
7841  * Based on:
7842  * Ext JS Library 1.1.1
7843  * Copyright(c) 2006-2007, Ext JS, LLC.
7844  *
7845  * Originally Released Under LGPL - original licence link has changed is not relivant.
7846  *
7847  * Fork - LGPL
7848  * <script type="text/javascript">
7849  */
7850
7851 /**
7852  * @class Roo.grid.AbstractSelectionModel
7853  * @extends Roo.util.Observable
7854  * @abstract
7855  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7856  * implemented by descendant classes.  This class should not be directly instantiated.
7857  * @constructor
7858  */
7859 Roo.grid.AbstractSelectionModel = function(){
7860     this.locked = false;
7861     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7862 };
7863
7864 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7865     /** @ignore Called by the grid automatically. Do not call directly. */
7866     init : function(grid){
7867         this.grid = grid;
7868         this.initEvents();
7869     },
7870
7871     /**
7872      * Locks the selections.
7873      */
7874     lock : function(){
7875         this.locked = true;
7876     },
7877
7878     /**
7879      * Unlocks the selections.
7880      */
7881     unlock : function(){
7882         this.locked = false;
7883     },
7884
7885     /**
7886      * Returns true if the selections are locked.
7887      * @return {Boolean}
7888      */
7889     isLocked : function(){
7890         return this.locked;
7891     }
7892 });/*
7893  * Based on:
7894  * Ext JS Library 1.1.1
7895  * Copyright(c) 2006-2007, Ext JS, LLC.
7896  *
7897  * Originally Released Under LGPL - original licence link has changed is not relivant.
7898  *
7899  * Fork - LGPL
7900  * <script type="text/javascript">
7901  */
7902 /**
7903  * @extends Roo.grid.AbstractSelectionModel
7904  * @class Roo.grid.RowSelectionModel
7905  * The default SelectionModel used by {@link Roo.grid.Grid}.
7906  * It supports multiple selections and keyboard selection/navigation. 
7907  * @constructor
7908  * @param {Object} config
7909  */
7910 Roo.grid.RowSelectionModel = function(config){
7911     Roo.apply(this, config);
7912     this.selections = new Roo.util.MixedCollection(false, function(o){
7913         return o.id;
7914     });
7915
7916     this.last = false;
7917     this.lastActive = false;
7918
7919     this.addEvents({
7920         /**
7921         * @event selectionchange
7922         * Fires when the selection changes
7923         * @param {SelectionModel} this
7924         */
7925        "selectionchange" : true,
7926        /**
7927         * @event afterselectionchange
7928         * Fires after the selection changes (eg. by key press or clicking)
7929         * @param {SelectionModel} this
7930         */
7931        "afterselectionchange" : true,
7932        /**
7933         * @event beforerowselect
7934         * Fires when a row is selected being selected, return false to cancel.
7935         * @param {SelectionModel} this
7936         * @param {Number} rowIndex The selected index
7937         * @param {Boolean} keepExisting False if other selections will be cleared
7938         */
7939        "beforerowselect" : true,
7940        /**
7941         * @event rowselect
7942         * Fires when a row is selected.
7943         * @param {SelectionModel} this
7944         * @param {Number} rowIndex The selected index
7945         * @param {Roo.data.Record} r The record
7946         */
7947        "rowselect" : true,
7948        /**
7949         * @event rowdeselect
7950         * Fires when a row is deselected.
7951         * @param {SelectionModel} this
7952         * @param {Number} rowIndex The selected index
7953         */
7954         "rowdeselect" : true
7955     });
7956     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7957     this.locked = false;
7958 };
7959
7960 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7961     /**
7962      * @cfg {Boolean} singleSelect
7963      * True to allow selection of only one row at a time (defaults to false)
7964      */
7965     singleSelect : false,
7966
7967     // private
7968     initEvents : function(){
7969
7970         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7971             this.grid.on("mousedown", this.handleMouseDown, this);
7972         }else{ // allow click to work like normal
7973             this.grid.on("rowclick", this.handleDragableRowClick, this);
7974         }
7975         // bootstrap does not have a view..
7976         var view = this.grid.view ? this.grid.view : this.grid;
7977         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7978             "up" : function(e){
7979                 if(!e.shiftKey){
7980                     this.selectPrevious(e.shiftKey);
7981                 }else if(this.last !== false && this.lastActive !== false){
7982                     var last = this.last;
7983                     this.selectRange(this.last,  this.lastActive-1);
7984                     view.focusRow(this.lastActive);
7985                     if(last !== false){
7986                         this.last = last;
7987                     }
7988                 }else{
7989                     this.selectFirstRow();
7990                 }
7991                 this.fireEvent("afterselectionchange", this);
7992             },
7993             "down" : function(e){
7994                 if(!e.shiftKey){
7995                     this.selectNext(e.shiftKey);
7996                 }else if(this.last !== false && this.lastActive !== false){
7997                     var last = this.last;
7998                     this.selectRange(this.last,  this.lastActive+1);
7999                     view.focusRow(this.lastActive);
8000                     if(last !== false){
8001                         this.last = last;
8002                     }
8003                 }else{
8004                     this.selectFirstRow();
8005                 }
8006                 this.fireEvent("afterselectionchange", this);
8007             },
8008             scope: this
8009         });
8010
8011          
8012         view.on("refresh", this.onRefresh, this);
8013         view.on("rowupdated", this.onRowUpdated, this);
8014         view.on("rowremoved", this.onRemove, this);
8015     },
8016
8017     // private
8018     onRefresh : function(){
8019         var ds = this.grid.ds, i, v = this.grid.view;
8020         var s = this.selections;
8021         s.each(function(r){
8022             if((i = ds.indexOfId(r.id)) != -1){
8023                 v.onRowSelect(i);
8024                 s.add(ds.getAt(i)); // updating the selection relate data
8025             }else{
8026                 s.remove(r);
8027             }
8028         });
8029     },
8030
8031     // private
8032     onRemove : function(v, index, r){
8033         this.selections.remove(r);
8034     },
8035
8036     // private
8037     onRowUpdated : function(v, index, r){
8038         if(this.isSelected(r)){
8039             v.onRowSelect(index);
8040         }
8041     },
8042
8043     /**
8044      * Select records.
8045      * @param {Array} records The records to select
8046      * @param {Boolean} keepExisting (optional) True to keep existing selections
8047      */
8048     selectRecords : function(records, keepExisting){
8049         if(!keepExisting){
8050             this.clearSelections();
8051         }
8052         var ds = this.grid.ds;
8053         for(var i = 0, len = records.length; i < len; i++){
8054             this.selectRow(ds.indexOf(records[i]), true);
8055         }
8056     },
8057
8058     /**
8059      * Gets the number of selected rows.
8060      * @return {Number}
8061      */
8062     getCount : function(){
8063         return this.selections.length;
8064     },
8065
8066     /**
8067      * Selects the first row in the grid.
8068      */
8069     selectFirstRow : function(){
8070         this.selectRow(0);
8071     },
8072
8073     /**
8074      * Select the last row.
8075      * @param {Boolean} keepExisting (optional) True to keep existing selections
8076      */
8077     selectLastRow : function(keepExisting){
8078         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8079     },
8080
8081     /**
8082      * Selects the row immediately following the last selected row.
8083      * @param {Boolean} keepExisting (optional) True to keep existing selections
8084      */
8085     selectNext : function(keepExisting){
8086         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8087             this.selectRow(this.last+1, keepExisting);
8088             var view = this.grid.view ? this.grid.view : this.grid;
8089             view.focusRow(this.last);
8090         }
8091     },
8092
8093     /**
8094      * Selects the row that precedes the last selected row.
8095      * @param {Boolean} keepExisting (optional) True to keep existing selections
8096      */
8097     selectPrevious : function(keepExisting){
8098         if(this.last){
8099             this.selectRow(this.last-1, keepExisting);
8100             var view = this.grid.view ? this.grid.view : this.grid;
8101             view.focusRow(this.last);
8102         }
8103     },
8104
8105     /**
8106      * Returns the selected records
8107      * @return {Array} Array of selected records
8108      */
8109     getSelections : function(){
8110         return [].concat(this.selections.items);
8111     },
8112
8113     /**
8114      * Returns the first selected record.
8115      * @return {Record}
8116      */
8117     getSelected : function(){
8118         return this.selections.itemAt(0);
8119     },
8120
8121
8122     /**
8123      * Clears all selections.
8124      */
8125     clearSelections : function(fast){
8126         if(this.locked) {
8127             return;
8128         }
8129         if(fast !== true){
8130             var ds = this.grid.ds;
8131             var s = this.selections;
8132             s.each(function(r){
8133                 this.deselectRow(ds.indexOfId(r.id));
8134             }, this);
8135             s.clear();
8136         }else{
8137             this.selections.clear();
8138         }
8139         this.last = false;
8140     },
8141
8142
8143     /**
8144      * Selects all rows.
8145      */
8146     selectAll : function(){
8147         if(this.locked) {
8148             return;
8149         }
8150         this.selections.clear();
8151         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8152             this.selectRow(i, true);
8153         }
8154     },
8155
8156     /**
8157      * Returns True if there is a selection.
8158      * @return {Boolean}
8159      */
8160     hasSelection : function(){
8161         return this.selections.length > 0;
8162     },
8163
8164     /**
8165      * Returns True if the specified row is selected.
8166      * @param {Number/Record} record The record or index of the record to check
8167      * @return {Boolean}
8168      */
8169     isSelected : function(index){
8170         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8171         return (r && this.selections.key(r.id) ? true : false);
8172     },
8173
8174     /**
8175      * Returns True if the specified record id is selected.
8176      * @param {String} id The id of record to check
8177      * @return {Boolean}
8178      */
8179     isIdSelected : function(id){
8180         return (this.selections.key(id) ? true : false);
8181     },
8182
8183     // private
8184     handleMouseDown : function(e, t)
8185     {
8186         var view = this.grid.view ? this.grid.view : this.grid;
8187         var rowIndex;
8188         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8189             return;
8190         };
8191         if(e.shiftKey && this.last !== false){
8192             var last = this.last;
8193             this.selectRange(last, rowIndex, e.ctrlKey);
8194             this.last = last; // reset the last
8195             view.focusRow(rowIndex);
8196         }else{
8197             var isSelected = this.isSelected(rowIndex);
8198             if(e.button !== 0 && isSelected){
8199                 view.focusRow(rowIndex);
8200             }else if(e.ctrlKey && isSelected){
8201                 this.deselectRow(rowIndex);
8202             }else if(!isSelected){
8203                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8204                 view.focusRow(rowIndex);
8205             }
8206         }
8207         this.fireEvent("afterselectionchange", this);
8208     },
8209     // private
8210     handleDragableRowClick :  function(grid, rowIndex, e) 
8211     {
8212         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8213             this.selectRow(rowIndex, false);
8214             var view = this.grid.view ? this.grid.view : this.grid;
8215             view.focusRow(rowIndex);
8216              this.fireEvent("afterselectionchange", this);
8217         }
8218     },
8219     
8220     /**
8221      * Selects multiple rows.
8222      * @param {Array} rows Array of the indexes of the row to select
8223      * @param {Boolean} keepExisting (optional) True to keep existing selections
8224      */
8225     selectRows : function(rows, keepExisting){
8226         if(!keepExisting){
8227             this.clearSelections();
8228         }
8229         for(var i = 0, len = rows.length; i < len; i++){
8230             this.selectRow(rows[i], true);
8231         }
8232     },
8233
8234     /**
8235      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8236      * @param {Number} startRow The index of the first row in the range
8237      * @param {Number} endRow The index of the last row in the range
8238      * @param {Boolean} keepExisting (optional) True to retain existing selections
8239      */
8240     selectRange : function(startRow, endRow, keepExisting){
8241         if(this.locked) {
8242             return;
8243         }
8244         if(!keepExisting){
8245             this.clearSelections();
8246         }
8247         if(startRow <= endRow){
8248             for(var i = startRow; i <= endRow; i++){
8249                 this.selectRow(i, true);
8250             }
8251         }else{
8252             for(var i = startRow; i >= endRow; i--){
8253                 this.selectRow(i, true);
8254             }
8255         }
8256     },
8257
8258     /**
8259      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8260      * @param {Number} startRow The index of the first row in the range
8261      * @param {Number} endRow The index of the last row in the range
8262      */
8263     deselectRange : function(startRow, endRow, preventViewNotify){
8264         if(this.locked) {
8265             return;
8266         }
8267         for(var i = startRow; i <= endRow; i++){
8268             this.deselectRow(i, preventViewNotify);
8269         }
8270     },
8271
8272     /**
8273      * Selects a row.
8274      * @param {Number} row The index of the row to select
8275      * @param {Boolean} keepExisting (optional) True to keep existing selections
8276      */
8277     selectRow : function(index, keepExisting, preventViewNotify){
8278         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8279             return;
8280         }
8281         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8282             if(!keepExisting || this.singleSelect){
8283                 this.clearSelections();
8284             }
8285             var r = this.grid.ds.getAt(index);
8286             this.selections.add(r);
8287             this.last = this.lastActive = index;
8288             if(!preventViewNotify){
8289                 var view = this.grid.view ? this.grid.view : this.grid;
8290                 view.onRowSelect(index);
8291             }
8292             this.fireEvent("rowselect", this, index, r);
8293             this.fireEvent("selectionchange", this);
8294         }
8295     },
8296
8297     /**
8298      * Deselects a row.
8299      * @param {Number} row The index of the row to deselect
8300      */
8301     deselectRow : function(index, preventViewNotify){
8302         if(this.locked) {
8303             return;
8304         }
8305         if(this.last == index){
8306             this.last = false;
8307         }
8308         if(this.lastActive == index){
8309             this.lastActive = false;
8310         }
8311         var r = this.grid.ds.getAt(index);
8312         this.selections.remove(r);
8313         if(!preventViewNotify){
8314             var view = this.grid.view ? this.grid.view : this.grid;
8315             view.onRowDeselect(index);
8316         }
8317         this.fireEvent("rowdeselect", this, index);
8318         this.fireEvent("selectionchange", this);
8319     },
8320
8321     // private
8322     restoreLast : function(){
8323         if(this._last){
8324             this.last = this._last;
8325         }
8326     },
8327
8328     // private
8329     acceptsNav : function(row, col, cm){
8330         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8331     },
8332
8333     // private
8334     onEditorKey : function(field, e){
8335         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8336         if(k == e.TAB){
8337             e.stopEvent();
8338             ed.completeEdit();
8339             if(e.shiftKey){
8340                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8341             }else{
8342                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8343             }
8344         }else if(k == e.ENTER && !e.ctrlKey){
8345             e.stopEvent();
8346             ed.completeEdit();
8347             if(e.shiftKey){
8348                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8349             }else{
8350                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8351             }
8352         }else if(k == e.ESC){
8353             ed.cancelEdit();
8354         }
8355         if(newCell){
8356             g.startEditing(newCell[0], newCell[1]);
8357         }
8358     }
8359 });/*
8360  * Based on:
8361  * Ext JS Library 1.1.1
8362  * Copyright(c) 2006-2007, Ext JS, LLC.
8363  *
8364  * Originally Released Under LGPL - original licence link has changed is not relivant.
8365  *
8366  * Fork - LGPL
8367  * <script type="text/javascript">
8368  */
8369  
8370
8371 /**
8372  * @class Roo.grid.ColumnModel
8373  * @extends Roo.util.Observable
8374  * This is the default implementation of a ColumnModel used by the Grid. It defines
8375  * the columns in the grid.
8376  * <br>Usage:<br>
8377  <pre><code>
8378  var colModel = new Roo.grid.ColumnModel([
8379         {header: "Ticker", width: 60, sortable: true, locked: true},
8380         {header: "Company Name", width: 150, sortable: true},
8381         {header: "Market Cap.", width: 100, sortable: true},
8382         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8383         {header: "Employees", width: 100, sortable: true, resizable: false}
8384  ]);
8385  </code></pre>
8386  * <p>
8387  
8388  * The config options listed for this class are options which may appear in each
8389  * individual column definition.
8390  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8391  * @constructor
8392  * @param {Object} config An Array of column config objects. See this class's
8393  * config objects for details.
8394 */
8395 Roo.grid.ColumnModel = function(config){
8396         /**
8397      * The config passed into the constructor
8398      */
8399     this.config = []; //config;
8400     this.lookup = {};
8401
8402     // if no id, create one
8403     // if the column does not have a dataIndex mapping,
8404     // map it to the order it is in the config
8405     for(var i = 0, len = config.length; i < len; i++){
8406         this.addColumn(config[i]);
8407         
8408     }
8409
8410     /**
8411      * The width of columns which have no width specified (defaults to 100)
8412      * @type Number
8413      */
8414     this.defaultWidth = 100;
8415
8416     /**
8417      * Default sortable of columns which have no sortable specified (defaults to false)
8418      * @type Boolean
8419      */
8420     this.defaultSortable = false;
8421
8422     this.addEvents({
8423         /**
8424              * @event widthchange
8425              * Fires when the width of a column changes.
8426              * @param {ColumnModel} this
8427              * @param {Number} columnIndex The column index
8428              * @param {Number} newWidth The new width
8429              */
8430             "widthchange": true,
8431         /**
8432              * @event headerchange
8433              * Fires when the text of a header changes.
8434              * @param {ColumnModel} this
8435              * @param {Number} columnIndex The column index
8436              * @param {Number} newText The new header text
8437              */
8438             "headerchange": true,
8439         /**
8440              * @event hiddenchange
8441              * Fires when a column is hidden or "unhidden".
8442              * @param {ColumnModel} this
8443              * @param {Number} columnIndex The column index
8444              * @param {Boolean} hidden true if hidden, false otherwise
8445              */
8446             "hiddenchange": true,
8447             /**
8448          * @event columnmoved
8449          * Fires when a column is moved.
8450          * @param {ColumnModel} this
8451          * @param {Number} oldIndex
8452          * @param {Number} newIndex
8453          */
8454         "columnmoved" : true,
8455         /**
8456          * @event columlockchange
8457          * Fires when a column's locked state is changed
8458          * @param {ColumnModel} this
8459          * @param {Number} colIndex
8460          * @param {Boolean} locked true if locked
8461          */
8462         "columnlockchange" : true
8463     });
8464     Roo.grid.ColumnModel.superclass.constructor.call(this);
8465 };
8466 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8467     /**
8468      * @cfg {String} header [required] The header text to display in the Grid view.
8469      */
8470         /**
8471      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8472      */
8473         /**
8474      * @cfg {String} smHeader Header at Bootsrap Small width
8475      */
8476         /**
8477      * @cfg {String} mdHeader Header at Bootsrap Medium width
8478      */
8479         /**
8480      * @cfg {String} lgHeader Header at Bootsrap Large width
8481      */
8482         /**
8483      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8484      */
8485     /**
8486      * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
8487      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8488      * specified, the column's index is used as an index into the Record's data Array.
8489      */
8490     /**
8491      * @cfg {Number} width  The initial width in pixels of the column. Using this
8492      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8493      */
8494     /**
8495      * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
8496      * Defaults to the value of the {@link #defaultSortable} property.
8497      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8498      */
8499     /**
8500      * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
8501      */
8502     /**
8503      * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
8504      */
8505     /**
8506      * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
8507      */
8508     /**
8509      * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
8510      */
8511     /**
8512      * @cfg {Function} renderer A function used to generate HTML markup for a cell
8513      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8514      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8515      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8516      */
8517        /**
8518      * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
8519      */
8520     /**
8521      * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
8522      */
8523     /**
8524      * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
8525      */
8526     /**
8527      * @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)
8528      */
8529     /**
8530      * @cfg {String} tooltip mouse over tooltip text
8531      */
8532     /**
8533      * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
8534      */
8535     /**
8536      * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
8537      */
8538     /**
8539      * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
8540      */
8541     /**
8542      * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
8543      */
8544         /**
8545      * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
8546      */
8547     /**
8548      * Returns the id of the column at the specified index.
8549      * @param {Number} index The column index
8550      * @return {String} the id
8551      */
8552     getColumnId : function(index){
8553         return this.config[index].id;
8554     },
8555
8556     /**
8557      * Returns the column for a specified id.
8558      * @param {String} id The column id
8559      * @return {Object} the column
8560      */
8561     getColumnById : function(id){
8562         return this.lookup[id];
8563     },
8564
8565     
8566     /**
8567      * Returns the column Object for a specified dataIndex.
8568      * @param {String} dataIndex The column dataIndex
8569      * @return {Object|Boolean} the column or false if not found
8570      */
8571     getColumnByDataIndex: function(dataIndex){
8572         var index = this.findColumnIndex(dataIndex);
8573         return index > -1 ? this.config[index] : false;
8574     },
8575     
8576     /**
8577      * Returns the index for a specified column id.
8578      * @param {String} id The column id
8579      * @return {Number} the index, or -1 if not found
8580      */
8581     getIndexById : function(id){
8582         for(var i = 0, len = this.config.length; i < len; i++){
8583             if(this.config[i].id == id){
8584                 return i;
8585             }
8586         }
8587         return -1;
8588     },
8589     
8590     /**
8591      * Returns the index for a specified column dataIndex.
8592      * @param {String} dataIndex The column dataIndex
8593      * @return {Number} the index, or -1 if not found
8594      */
8595     
8596     findColumnIndex : function(dataIndex){
8597         for(var i = 0, len = this.config.length; i < len; i++){
8598             if(this.config[i].dataIndex == dataIndex){
8599                 return i;
8600             }
8601         }
8602         return -1;
8603     },
8604     
8605     
8606     moveColumn : function(oldIndex, newIndex){
8607         var c = this.config[oldIndex];
8608         this.config.splice(oldIndex, 1);
8609         this.config.splice(newIndex, 0, c);
8610         this.dataMap = null;
8611         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8612     },
8613
8614     isLocked : function(colIndex){
8615         return this.config[colIndex].locked === true;
8616     },
8617
8618     setLocked : function(colIndex, value, suppressEvent){
8619         if(this.isLocked(colIndex) == value){
8620             return;
8621         }
8622         this.config[colIndex].locked = value;
8623         if(!suppressEvent){
8624             this.fireEvent("columnlockchange", this, colIndex, value);
8625         }
8626     },
8627
8628     getTotalLockedWidth : function(){
8629         var totalWidth = 0;
8630         for(var i = 0; i < this.config.length; i++){
8631             if(this.isLocked(i) && !this.isHidden(i)){
8632                 this.totalWidth += this.getColumnWidth(i);
8633             }
8634         }
8635         return totalWidth;
8636     },
8637
8638     getLockedCount : function(){
8639         for(var i = 0, len = this.config.length; i < len; i++){
8640             if(!this.isLocked(i)){
8641                 return i;
8642             }
8643         }
8644         
8645         return this.config.length;
8646     },
8647
8648     /**
8649      * Returns the number of columns.
8650      * @return {Number}
8651      */
8652     getColumnCount : function(visibleOnly){
8653         if(visibleOnly === true){
8654             var c = 0;
8655             for(var i = 0, len = this.config.length; i < len; i++){
8656                 if(!this.isHidden(i)){
8657                     c++;
8658                 }
8659             }
8660             return c;
8661         }
8662         return this.config.length;
8663     },
8664
8665     /**
8666      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8667      * @param {Function} fn
8668      * @param {Object} scope (optional)
8669      * @return {Array} result
8670      */
8671     getColumnsBy : function(fn, scope){
8672         var r = [];
8673         for(var i = 0, len = this.config.length; i < len; i++){
8674             var c = this.config[i];
8675             if(fn.call(scope||this, c, i) === true){
8676                 r[r.length] = c;
8677             }
8678         }
8679         return r;
8680     },
8681
8682     /**
8683      * Returns true if the specified column is sortable.
8684      * @param {Number} col The column index
8685      * @return {Boolean}
8686      */
8687     isSortable : function(col){
8688         if(typeof this.config[col].sortable == "undefined"){
8689             return this.defaultSortable;
8690         }
8691         return this.config[col].sortable;
8692     },
8693
8694     /**
8695      * Returns the rendering (formatting) function defined for the column.
8696      * @param {Number} col The column index.
8697      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8698      */
8699     getRenderer : function(col){
8700         if(!this.config[col].renderer){
8701             return Roo.grid.ColumnModel.defaultRenderer;
8702         }
8703         return this.config[col].renderer;
8704     },
8705
8706     /**
8707      * Sets the rendering (formatting) function for a column.
8708      * @param {Number} col The column index
8709      * @param {Function} fn The function to use to process the cell's raw data
8710      * to return HTML markup for the grid view. The render function is called with
8711      * the following parameters:<ul>
8712      * <li>Data value.</li>
8713      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8714      * <li>css A CSS style string to apply to the table cell.</li>
8715      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8716      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8717      * <li>Row index</li>
8718      * <li>Column index</li>
8719      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8720      */
8721     setRenderer : function(col, fn){
8722         this.config[col].renderer = fn;
8723     },
8724
8725     /**
8726      * Returns the width for the specified column.
8727      * @param {Number} col The column index
8728      * @param (optional) {String} gridSize bootstrap width size.
8729      * @return {Number}
8730      */
8731     getColumnWidth : function(col, gridSize)
8732         {
8733                 var cfg = this.config[col];
8734                 
8735                 if (typeof(gridSize) == 'undefined') {
8736                         return cfg.width * 1 || this.defaultWidth;
8737                 }
8738                 if (gridSize === false) { // if we set it..
8739                         return cfg.width || false;
8740                 }
8741                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8742                 
8743                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8744                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8745                                 continue;
8746                         }
8747                         return cfg[ sizes[i] ];
8748                 }
8749                 return 1;
8750                 
8751     },
8752
8753     /**
8754      * Sets the width for a column.
8755      * @param {Number} col The column index
8756      * @param {Number} width The new width
8757      */
8758     setColumnWidth : function(col, width, suppressEvent){
8759         this.config[col].width = width;
8760         this.totalWidth = null;
8761         if(!suppressEvent){
8762              this.fireEvent("widthchange", this, col, width);
8763         }
8764     },
8765
8766     /**
8767      * Returns the total width of all columns.
8768      * @param {Boolean} includeHidden True to include hidden column widths
8769      * @return {Number}
8770      */
8771     getTotalWidth : function(includeHidden){
8772         if(!this.totalWidth){
8773             this.totalWidth = 0;
8774             for(var i = 0, len = this.config.length; i < len; i++){
8775                 if(includeHidden || !this.isHidden(i)){
8776                     this.totalWidth += this.getColumnWidth(i);
8777                 }
8778             }
8779         }
8780         return this.totalWidth;
8781     },
8782
8783     /**
8784      * Returns the header for the specified column.
8785      * @param {Number} col The column index
8786      * @return {String}
8787      */
8788     getColumnHeader : function(col){
8789         return this.config[col].header;
8790     },
8791
8792     /**
8793      * Sets the header for a column.
8794      * @param {Number} col The column index
8795      * @param {String} header The new header
8796      */
8797     setColumnHeader : function(col, header){
8798         this.config[col].header = header;
8799         this.fireEvent("headerchange", this, col, header);
8800     },
8801
8802     /**
8803      * Returns the tooltip for the specified column.
8804      * @param {Number} col The column index
8805      * @return {String}
8806      */
8807     getColumnTooltip : function(col){
8808             return this.config[col].tooltip;
8809     },
8810     /**
8811      * Sets the tooltip for a column.
8812      * @param {Number} col The column index
8813      * @param {String} tooltip The new tooltip
8814      */
8815     setColumnTooltip : function(col, tooltip){
8816             this.config[col].tooltip = tooltip;
8817     },
8818
8819     /**
8820      * Returns the dataIndex for the specified column.
8821      * @param {Number} col The column index
8822      * @return {Number}
8823      */
8824     getDataIndex : function(col){
8825         return this.config[col].dataIndex;
8826     },
8827
8828     /**
8829      * Sets the dataIndex for a column.
8830      * @param {Number} col The column index
8831      * @param {Number} dataIndex The new dataIndex
8832      */
8833     setDataIndex : function(col, dataIndex){
8834         this.config[col].dataIndex = dataIndex;
8835     },
8836
8837     
8838     
8839     /**
8840      * Returns true if the cell is editable.
8841      * @param {Number} colIndex The column index
8842      * @param {Number} rowIndex The row index - this is nto actually used..?
8843      * @return {Boolean}
8844      */
8845     isCellEditable : function(colIndex, rowIndex){
8846         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8847     },
8848
8849     /**
8850      * Returns the editor defined for the cell/column.
8851      * return false or null to disable editing.
8852      * @param {Number} colIndex The column index
8853      * @param {Number} rowIndex The row index
8854      * @return {Object}
8855      */
8856     getCellEditor : function(colIndex, rowIndex){
8857         return this.config[colIndex].editor;
8858     },
8859
8860     /**
8861      * Sets if a column is editable.
8862      * @param {Number} col The column index
8863      * @param {Boolean} editable True if the column is editable
8864      */
8865     setEditable : function(col, editable){
8866         this.config[col].editable = editable;
8867     },
8868
8869
8870     /**
8871      * Returns true if the column is hidden.
8872      * @param {Number} colIndex The column index
8873      * @return {Boolean}
8874      */
8875     isHidden : function(colIndex){
8876         return this.config[colIndex].hidden;
8877     },
8878
8879
8880     /**
8881      * Returns true if the column width cannot be changed
8882      */
8883     isFixed : function(colIndex){
8884         return this.config[colIndex].fixed;
8885     },
8886
8887     /**
8888      * Returns true if the column can be resized
8889      * @return {Boolean}
8890      */
8891     isResizable : function(colIndex){
8892         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8893     },
8894     /**
8895      * Sets if a column is hidden.
8896      * @param {Number} colIndex The column index
8897      * @param {Boolean} hidden True if the column is hidden
8898      */
8899     setHidden : function(colIndex, hidden){
8900         this.config[colIndex].hidden = hidden;
8901         this.totalWidth = null;
8902         this.fireEvent("hiddenchange", this, colIndex, hidden);
8903     },
8904
8905     /**
8906      * Sets the editor for a column.
8907      * @param {Number} col The column index
8908      * @param {Object} editor The editor object
8909      */
8910     setEditor : function(col, editor){
8911         this.config[col].editor = editor;
8912     },
8913     /**
8914      * Add a column (experimental...) - defaults to adding to the end..
8915      * @param {Object} config 
8916     */
8917     addColumn : function(c)
8918     {
8919     
8920         var i = this.config.length;
8921         this.config[i] = c;
8922         
8923         if(typeof c.dataIndex == "undefined"){
8924             c.dataIndex = i;
8925         }
8926         if(typeof c.renderer == "string"){
8927             c.renderer = Roo.util.Format[c.renderer];
8928         }
8929         if(typeof c.id == "undefined"){
8930             c.id = Roo.id();
8931         }
8932         if(c.editor && c.editor.xtype){
8933             c.editor  = Roo.factory(c.editor, Roo.grid);
8934         }
8935         if(c.editor && c.editor.isFormField){
8936             c.editor = new Roo.grid.GridEditor(c.editor);
8937         }
8938         this.lookup[c.id] = c;
8939     }
8940     
8941 });
8942
8943 Roo.grid.ColumnModel.defaultRenderer = function(value)
8944 {
8945     if(typeof value == "object") {
8946         return value;
8947     }
8948         if(typeof value == "string" && value.length < 1){
8949             return "&#160;";
8950         }
8951     
8952         return String.format("{0}", value);
8953 };
8954
8955 // Alias for backwards compatibility
8956 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8957 /*
8958  * Based on:
8959  * Ext JS Library 1.1.1
8960  * Copyright(c) 2006-2007, Ext JS, LLC.
8961  *
8962  * Originally Released Under LGPL - original licence link has changed is not relivant.
8963  *
8964  * Fork - LGPL
8965  * <script type="text/javascript">
8966  */
8967  
8968 /**
8969  * @class Roo.LoadMask
8970  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8971  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8972  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8973  * element's UpdateManager load indicator and will be destroyed after the initial load.
8974  * @constructor
8975  * Create a new LoadMask
8976  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8977  * @param {Object} config The config object
8978  */
8979 Roo.LoadMask = function(el, config){
8980     this.el = Roo.get(el);
8981     Roo.apply(this, config);
8982     if(this.store){
8983         this.store.on('beforeload', this.onBeforeLoad, this);
8984         this.store.on('load', this.onLoad, this);
8985         this.store.on('loadexception', this.onLoadException, this);
8986         this.removeMask = false;
8987     }else{
8988         var um = this.el.getUpdateManager();
8989         um.showLoadIndicator = false; // disable the default indicator
8990         um.on('beforeupdate', this.onBeforeLoad, this);
8991         um.on('update', this.onLoad, this);
8992         um.on('failure', this.onLoad, this);
8993         this.removeMask = true;
8994     }
8995 };
8996
8997 Roo.LoadMask.prototype = {
8998     /**
8999      * @cfg {Boolean} removeMask
9000      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
9001      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
9002      */
9003     removeMask : false,
9004     /**
9005      * @cfg {String} msg
9006      * The text to display in a centered loading message box (defaults to 'Loading...')
9007      */
9008     msg : 'Loading...',
9009     /**
9010      * @cfg {String} msgCls
9011      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9012      */
9013     msgCls : 'x-mask-loading',
9014
9015     /**
9016      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9017      * @type Boolean
9018      */
9019     disabled: false,
9020
9021     /**
9022      * Disables the mask to prevent it from being displayed
9023      */
9024     disable : function(){
9025        this.disabled = true;
9026     },
9027
9028     /**
9029      * Enables the mask so that it can be displayed
9030      */
9031     enable : function(){
9032         this.disabled = false;
9033     },
9034     
9035     onLoadException : function()
9036     {
9037         Roo.log(arguments);
9038         
9039         if (typeof(arguments[3]) != 'undefined') {
9040             Roo.MessageBox.alert("Error loading",arguments[3]);
9041         } 
9042         /*
9043         try {
9044             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9045                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9046             }   
9047         } catch(e) {
9048             
9049         }
9050         */
9051     
9052         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9053     },
9054     // private
9055     onLoad : function()
9056     {
9057         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9058     },
9059
9060     // private
9061     onBeforeLoad : function(){
9062         if(!this.disabled){
9063             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9064         }
9065     },
9066
9067     // private
9068     destroy : function(){
9069         if(this.store){
9070             this.store.un('beforeload', this.onBeforeLoad, this);
9071             this.store.un('load', this.onLoad, this);
9072             this.store.un('loadexception', this.onLoadException, this);
9073         }else{
9074             var um = this.el.getUpdateManager();
9075             um.un('beforeupdate', this.onBeforeLoad, this);
9076             um.un('update', this.onLoad, this);
9077             um.un('failure', this.onLoad, this);
9078         }
9079     }
9080 };/**
9081  * @class Roo.bootstrap.Table
9082  * @licence LGBL
9083  * @extends Roo.bootstrap.Component
9084  * @children Roo.bootstrap.TableBody
9085  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9086  * Similar to Roo.grid.Grid
9087  * <pre><code>
9088  var table = Roo.factory({
9089     xtype : 'Table',
9090     xns : Roo.bootstrap,
9091     autoSizeColumns: true,
9092     
9093     
9094     store : {
9095         xtype : 'Store',
9096         xns : Roo.data,
9097         remoteSort : true,
9098         sortInfo : { direction : 'ASC', field: 'name' },
9099         proxy : {
9100            xtype : 'HttpProxy',
9101            xns : Roo.data,
9102            method : 'GET',
9103            url : 'https://example.com/some.data.url.json'
9104         },
9105         reader : {
9106            xtype : 'JsonReader',
9107            xns : Roo.data,
9108            fields : [ 'id', 'name', whatever' ],
9109            id : 'id',
9110            root : 'data'
9111         }
9112     },
9113     cm : [
9114         {
9115             xtype : 'ColumnModel',
9116             xns : Roo.grid,
9117             align : 'center',
9118             cursor : 'pointer',
9119             dataIndex : 'is_in_group',
9120             header : "Name",
9121             sortable : true,
9122             renderer : function(v, x , r) {  
9123             
9124                 return String.format("{0}", v)
9125             }
9126             width : 3
9127         } // more columns..
9128     ],
9129     selModel : {
9130         xtype : 'RowSelectionModel',
9131         xns : Roo.bootstrap.Table
9132         // you can add listeners to catch selection change here....
9133     }
9134      
9135
9136  });
9137  // set any options
9138  grid.render(Roo.get("some-div"));
9139 </code></pre>
9140
9141 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9142
9143
9144
9145  *
9146  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9147  * @cfg {Roo.data.Store} store The data store to use
9148  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9149  * 
9150  * @cfg {String} cls table class
9151  *
9152  *
9153  * @cfg {string} empty_results  Text to display for no results 
9154  * @cfg {boolean} striped Should the rows be alternative striped
9155  * @cfg {boolean} bordered Add borders to the table
9156  * @cfg {boolean} hover Add hover highlighting
9157  * @cfg {boolean} condensed Format condensed
9158  * @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,
9159  *                also adds table-responsive (see bootstrap docs for details)
9160  * @cfg {Boolean} loadMask (true|false) default false
9161  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9162  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9163  * @cfg {Boolean} rowSelection (true|false) default false
9164  * @cfg {Boolean} cellSelection (true|false) default false
9165  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9166  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9167  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9168  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9169  * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9170  *
9171  * 
9172  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9173  * 
9174  * @constructor
9175  * Create a new Table
9176  * @param {Object} config The config object
9177  */
9178
9179 Roo.bootstrap.Table = function(config)
9180 {
9181     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9182      
9183     // BC...
9184     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9185     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9186     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9187     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9188     
9189     this.view = this; // compat with grid.
9190     
9191     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9192     if (this.sm) {
9193         this.sm.grid = this;
9194         this.selModel = Roo.factory(this.sm, Roo.grid);
9195         this.sm = this.selModel;
9196         this.sm.xmodule = this.xmodule || false;
9197     }
9198     
9199     if (this.cm && typeof(this.cm.config) == 'undefined') {
9200         this.colModel = new Roo.grid.ColumnModel(this.cm);
9201         this.cm = this.colModel;
9202         this.cm.xmodule = this.xmodule || false;
9203     }
9204     if (this.store) {
9205         this.store= Roo.factory(this.store, Roo.data);
9206         this.ds = this.store;
9207         this.ds.xmodule = this.xmodule || false;
9208          
9209     }
9210     if (this.footer && this.store) {
9211         this.footer.dataSource = this.ds;
9212         this.footer = Roo.factory(this.footer);
9213     }
9214     
9215     /** @private */
9216     this.addEvents({
9217         /**
9218          * @event cellclick
9219          * Fires when a cell is clicked
9220          * @param {Roo.bootstrap.Table} this
9221          * @param {Roo.Element} el
9222          * @param {Number} rowIndex
9223          * @param {Number} columnIndex
9224          * @param {Roo.EventObject} e
9225          */
9226         "cellclick" : true,
9227         /**
9228          * @event celldblclick
9229          * Fires when a cell is double clicked
9230          * @param {Roo.bootstrap.Table} this
9231          * @param {Roo.Element} el
9232          * @param {Number} rowIndex
9233          * @param {Number} columnIndex
9234          * @param {Roo.EventObject} e
9235          */
9236         "celldblclick" : true,
9237         /**
9238          * @event rowclick
9239          * Fires when a row is clicked
9240          * @param {Roo.bootstrap.Table} this
9241          * @param {Roo.Element} el
9242          * @param {Number} rowIndex
9243          * @param {Roo.EventObject} e
9244          */
9245         "rowclick" : true,
9246         /**
9247          * @event rowdblclick
9248          * Fires when a row is double clicked
9249          * @param {Roo.bootstrap.Table} this
9250          * @param {Roo.Element} el
9251          * @param {Number} rowIndex
9252          * @param {Roo.EventObject} e
9253          */
9254         "rowdblclick" : true,
9255         /**
9256          * @event mouseover
9257          * Fires when a mouseover occur
9258          * @param {Roo.bootstrap.Table} this
9259          * @param {Roo.Element} el
9260          * @param {Number} rowIndex
9261          * @param {Number} columnIndex
9262          * @param {Roo.EventObject} e
9263          */
9264         "mouseover" : true,
9265         /**
9266          * @event mouseout
9267          * Fires when a mouseout occur
9268          * @param {Roo.bootstrap.Table} this
9269          * @param {Roo.Element} el
9270          * @param {Number} rowIndex
9271          * @param {Number} columnIndex
9272          * @param {Roo.EventObject} e
9273          */
9274         "mouseout" : true,
9275         /**
9276          * @event rowclass
9277          * Fires when a row is rendered, so you can change add a style to it.
9278          * @param {Roo.bootstrap.Table} this
9279          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9280          */
9281         'rowclass' : true,
9282           /**
9283          * @event rowsrendered
9284          * Fires when all the  rows have been rendered
9285          * @param {Roo.bootstrap.Table} this
9286          */
9287         'rowsrendered' : true,
9288         /**
9289          * @event contextmenu
9290          * The raw contextmenu event for the entire grid.
9291          * @param {Roo.EventObject} e
9292          */
9293         "contextmenu" : true,
9294         /**
9295          * @event rowcontextmenu
9296          * Fires when a row is right clicked
9297          * @param {Roo.bootstrap.Table} this
9298          * @param {Number} rowIndex
9299          * @param {Roo.EventObject} e
9300          */
9301         "rowcontextmenu" : true,
9302         /**
9303          * @event cellcontextmenu
9304          * Fires when a cell is right clicked
9305          * @param {Roo.bootstrap.Table} this
9306          * @param {Number} rowIndex
9307          * @param {Number} cellIndex
9308          * @param {Roo.EventObject} e
9309          */
9310          "cellcontextmenu" : true,
9311          /**
9312          * @event headercontextmenu
9313          * Fires when a header is right clicked
9314          * @param {Roo.bootstrap.Table} this
9315          * @param {Number} columnIndex
9316          * @param {Roo.EventObject} e
9317          */
9318         "headercontextmenu" : true,
9319         /**
9320          * @event mousedown
9321          * The raw mousedown event for the entire grid.
9322          * @param {Roo.EventObject} e
9323          */
9324         "mousedown" : true
9325         
9326     });
9327 };
9328
9329 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9330     
9331     cls: false,
9332     
9333     empty_results : '',
9334     striped : false,
9335     scrollBody : false,
9336     bordered: false,
9337     hover:  false,
9338     condensed : false,
9339     responsive : false,
9340     sm : false,
9341     cm : false,
9342     store : false,
9343     loadMask : false,
9344     footerShow : true,
9345     headerShow : true,
9346     enableColumnResize: true,
9347   
9348     rowSelection : false,
9349     cellSelection : false,
9350     layout : false,
9351
9352     minColumnWidth : 50,
9353     
9354     // Roo.Element - the tbody
9355     bodyEl: false,  // <tbody> Roo.Element - thead element    
9356     headEl: false,  // <thead> Roo.Element - thead element
9357     resizeProxy : false, // proxy element for dragging?
9358
9359
9360     
9361     container: false, // used by gridpanel...
9362     
9363     lazyLoad : false,
9364     
9365     CSS : Roo.util.CSS,
9366     
9367     auto_hide_footer : false,
9368     
9369     view: false, // actually points to this..
9370     
9371     getAutoCreate : function()
9372     {
9373         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9374         
9375         cfg = {
9376             tag: 'table',
9377             cls : 'table', 
9378             cn : []
9379         };
9380         // this get's auto added by panel.Grid
9381         if (this.scrollBody) {
9382             cfg.cls += ' table-body-fixed';
9383         }    
9384         if (this.striped) {
9385             cfg.cls += ' table-striped';
9386         }
9387         
9388         if (this.hover) {
9389             cfg.cls += ' table-hover';
9390         }
9391         if (this.bordered) {
9392             cfg.cls += ' table-bordered';
9393         }
9394         if (this.condensed) {
9395             cfg.cls += ' table-condensed';
9396         }
9397         
9398         if (this.responsive) {
9399             cfg.cls += ' table-responsive';
9400         }
9401         
9402         if (this.cls) {
9403             cfg.cls+=  ' ' +this.cls;
9404         }
9405         
9406         
9407         
9408         if (this.layout) {
9409             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9410         }
9411         
9412         if(this.store || this.cm){
9413             if(this.headerShow){
9414                 cfg.cn.push(this.renderHeader());
9415             }
9416             
9417             cfg.cn.push(this.renderBody());
9418             
9419             if(this.footerShow){
9420                 cfg.cn.push(this.renderFooter());
9421             }
9422             // where does this come from?
9423             //cfg.cls+=  ' TableGrid';
9424         }
9425         
9426         return { cn : [ cfg ] };
9427     },
9428     
9429     initEvents : function()
9430     {   
9431         if(!this.store || !this.cm){
9432             return;
9433         }
9434         if (this.selModel) {
9435             this.selModel.initEvents();
9436         }
9437         
9438         
9439         //Roo.log('initEvents with ds!!!!');
9440         
9441         this.bodyEl = this.el.select('tbody', true).first();
9442         this.headEl = this.el.select('thead', true).first();
9443         this.mainFoot = this.el.select('tfoot', true).first();
9444         
9445         
9446         
9447         
9448         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9449             e.on('click', this.sort, this);
9450         }, this);
9451         
9452         
9453         // why is this done????? = it breaks dialogs??
9454         //this.parent().el.setStyle('position', 'relative');
9455         
9456         
9457         if (this.footer) {
9458             this.footer.parentId = this.id;
9459             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9460             
9461             if(this.lazyLoad){
9462                 this.el.select('tfoot tr td').first().addClass('hide');
9463             }
9464         } 
9465         
9466         if(this.loadMask) {
9467             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9468         }
9469         
9470         this.store.on('load', this.onLoad, this);
9471         this.store.on('beforeload', this.onBeforeLoad, this);
9472         this.store.on('update', this.onUpdate, this);
9473         this.store.on('add', this.onAdd, this);
9474         this.store.on("clear", this.clear, this);
9475         
9476         this.el.on("contextmenu", this.onContextMenu, this);
9477         
9478         
9479         this.cm.on("headerchange", this.onHeaderChange, this);
9480         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9481
9482  //?? does bodyEl get replaced on render?
9483         this.bodyEl.on("click", this.onClick, this);
9484         this.bodyEl.on("dblclick", this.onDblClick, this);        
9485         this.bodyEl.on('scroll', this.onBodyScroll, this);
9486
9487         // guessing mainbody will work - this relays usually caught by selmodel at present.
9488         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9489   
9490   
9491         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9492         
9493   
9494         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9495             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9496         }
9497         
9498         this.initCSS();
9499     },
9500     // Compatibility with grid - we implement all the view features at present.
9501     getView : function()
9502     {
9503         return this;
9504     },
9505     
9506     initCSS : function()
9507     {
9508         
9509         
9510         var cm = this.cm, styles = [];
9511         this.CSS.removeStyleSheet(this.id + '-cssrules');
9512         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9513         // we can honour xs/sm/md/xl  as widths...
9514         // we first have to decide what widht we are currently at...
9515         var sz = Roo.getGridSize();
9516         
9517         var total = 0;
9518         var last = -1;
9519         var cols = []; // visable cols.
9520         var total_abs = 0;
9521         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9522             var w = cm.getColumnWidth(i, false);
9523             if(cm.isHidden(i)){
9524                 cols.push( { rel : false, abs : 0 });
9525                 continue;
9526             }
9527             if (w !== false) {
9528                 cols.push( { rel : false, abs : w });
9529                 total_abs += w;
9530                 last = i; // not really..
9531                 continue;
9532             }
9533             var w = cm.getColumnWidth(i, sz);
9534             if (w > 0) {
9535                 last = i
9536             }
9537             total += w;
9538             cols.push( { rel : w, abs : false });
9539         }
9540         
9541         var avail = this.bodyEl.dom.clientWidth - total_abs;
9542         
9543         var unitWidth = Math.floor(avail / total);
9544         var rem = avail - (unitWidth * total);
9545         
9546         var hidden, width, pos = 0 , splithide , left;
9547         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9548             
9549             hidden = 'display:none;';
9550             left = '';
9551             width  = 'width:0px;';
9552             splithide = '';
9553             if(!cm.isHidden(i)){
9554                 hidden = '';
9555                 
9556                 
9557                 // we can honour xs/sm/md/xl ?
9558                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9559                 if (w===0) {
9560                     hidden = 'display:none;';
9561                 }
9562                 // width should return a small number...
9563                 if (i == last) {
9564                     w+=rem; // add the remaining with..
9565                 }
9566                 pos += w;
9567                 left = "left:" + (pos -4) + "px;";
9568                 width = "width:" + w+ "px;";
9569                 
9570             }
9571             if (this.responsive) {
9572                 width = '';
9573                 left = '';
9574                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9575                 splithide = 'display: none;';
9576             }
9577             
9578             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9579             if (this.headEl) {
9580                 if (i == last) {
9581                     splithide = 'display:none;';
9582                 }
9583                 
9584                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9585                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9586                             // this is the popover version..
9587                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9588                 );
9589             }
9590             
9591         }
9592         //Roo.log(styles.join(''));
9593         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9594         
9595     },
9596     
9597     
9598     
9599     onContextMenu : function(e, t)
9600     {
9601         this.processEvent("contextmenu", e);
9602     },
9603     
9604     processEvent : function(name, e)
9605     {
9606         if (name != 'touchstart' ) {
9607             this.fireEvent(name, e);    
9608         }
9609         
9610         var t = e.getTarget();
9611         
9612         var cell = Roo.get(t);
9613         
9614         if(!cell){
9615             return;
9616         }
9617         
9618         if(cell.findParent('tfoot', false, true)){
9619             return;
9620         }
9621         
9622         if(cell.findParent('thead', false, true)){
9623             
9624             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9625                 cell = Roo.get(t).findParent('th', false, true);
9626                 if (!cell) {
9627                     Roo.log("failed to find th in thead?");
9628                     Roo.log(e.getTarget());
9629                     return;
9630                 }
9631             }
9632             
9633             var cellIndex = cell.dom.cellIndex;
9634             
9635             var ename = name == 'touchstart' ? 'click' : name;
9636             this.fireEvent("header" + ename, this, cellIndex, e);
9637             
9638             return;
9639         }
9640         
9641         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9642             cell = Roo.get(t).findParent('td', false, true);
9643             if (!cell) {
9644                 Roo.log("failed to find th in tbody?");
9645                 Roo.log(e.getTarget());
9646                 return;
9647             }
9648         }
9649         
9650         var row = cell.findParent('tr', false, true);
9651         var cellIndex = cell.dom.cellIndex;
9652         var rowIndex = row.dom.rowIndex - 1;
9653         
9654         if(row !== false){
9655             
9656             this.fireEvent("row" + name, this, rowIndex, e);
9657             
9658             if(cell !== false){
9659             
9660                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9661             }
9662         }
9663         
9664     },
9665     
9666     onMouseover : function(e, el)
9667     {
9668         var cell = Roo.get(el);
9669         
9670         if(!cell){
9671             return;
9672         }
9673         
9674         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9675             cell = cell.findParent('td', false, true);
9676         }
9677         
9678         var row = cell.findParent('tr', false, true);
9679         var cellIndex = cell.dom.cellIndex;
9680         var rowIndex = row.dom.rowIndex - 1; // start from 0
9681         
9682         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9683         
9684     },
9685     
9686     onMouseout : function(e, el)
9687     {
9688         var cell = Roo.get(el);
9689         
9690         if(!cell){
9691             return;
9692         }
9693         
9694         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9695             cell = cell.findParent('td', false, true);
9696         }
9697         
9698         var row = cell.findParent('tr', false, true);
9699         var cellIndex = cell.dom.cellIndex;
9700         var rowIndex = row.dom.rowIndex - 1; // start from 0
9701         
9702         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9703         
9704     },
9705     
9706     onClick : function(e, el)
9707     {
9708         var cell = Roo.get(el);
9709         
9710         if(!cell || (!this.cellSelection && !this.rowSelection)){
9711             return;
9712         }
9713         
9714         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9715             cell = cell.findParent('td', false, true);
9716         }
9717         
9718         if(!cell || typeof(cell) == 'undefined'){
9719             return;
9720         }
9721         
9722         var row = cell.findParent('tr', false, true);
9723         
9724         if(!row || typeof(row) == 'undefined'){
9725             return;
9726         }
9727         
9728         var cellIndex = cell.dom.cellIndex;
9729         var rowIndex = this.getRowIndex(row);
9730         
9731         // why??? - should these not be based on SelectionModel?
9732         //if(this.cellSelection){
9733             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9734         //}
9735         
9736         //if(this.rowSelection){
9737             this.fireEvent('rowclick', this, row, rowIndex, e);
9738         //}
9739          
9740     },
9741         
9742     onDblClick : function(e,el)
9743     {
9744         var cell = Roo.get(el);
9745         
9746         if(!cell || (!this.cellSelection && !this.rowSelection)){
9747             return;
9748         }
9749         
9750         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9751             cell = cell.findParent('td', false, true);
9752         }
9753         
9754         if(!cell || typeof(cell) == 'undefined'){
9755             return;
9756         }
9757         
9758         var row = cell.findParent('tr', false, true);
9759         
9760         if(!row || typeof(row) == 'undefined'){
9761             return;
9762         }
9763         
9764         var cellIndex = cell.dom.cellIndex;
9765         var rowIndex = this.getRowIndex(row);
9766         
9767         if(this.cellSelection){
9768             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9769         }
9770         
9771         if(this.rowSelection){
9772             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9773         }
9774     },
9775     findRowIndex : function(el)
9776     {
9777         var cell = Roo.get(el);
9778         if(!cell) {
9779             return false;
9780         }
9781         var row = cell.findParent('tr', false, true);
9782         
9783         if(!row || typeof(row) == 'undefined'){
9784             return false;
9785         }
9786         return this.getRowIndex(row);
9787     },
9788     sort : function(e,el)
9789     {
9790         var col = Roo.get(el);
9791         
9792         if(!col.hasClass('sortable')){
9793             return;
9794         }
9795         
9796         var sort = col.attr('sort');
9797         var dir = 'ASC';
9798         
9799         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9800             dir = 'DESC';
9801         }
9802         
9803         this.store.sortInfo = {field : sort, direction : dir};
9804         
9805         if (this.footer) {
9806             Roo.log("calling footer first");
9807             this.footer.onClick('first');
9808         } else {
9809         
9810             this.store.load({ params : { start : 0 } });
9811         }
9812     },
9813     
9814     renderHeader : function()
9815     {
9816         var header = {
9817             tag: 'thead',
9818             cn : []
9819         };
9820         
9821         var cm = this.cm;
9822         this.totalWidth = 0;
9823         
9824         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9825             
9826             var config = cm.config[i];
9827             
9828             var c = {
9829                 tag: 'th',
9830                 cls : 'x-hcol-' + i,
9831                 style : '',
9832                 
9833                 html: cm.getColumnHeader(i)
9834             };
9835             
9836             var tooltip = cm.getColumnTooltip(i);
9837             if (tooltip) {
9838                 c.tooltip = tooltip;
9839             }
9840             
9841             
9842             var hh = '';
9843             
9844             if(typeof(config.sortable) != 'undefined' && config.sortable){
9845                 c.cls += ' sortable';
9846                 c.html = '<i class="fa"></i>' + c.html;
9847             }
9848             
9849             // could use BS4 hidden-..-down 
9850             
9851             if(typeof(config.lgHeader) != 'undefined'){
9852                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9853             }
9854             
9855             if(typeof(config.mdHeader) != 'undefined'){
9856                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9857             }
9858             
9859             if(typeof(config.smHeader) != 'undefined'){
9860                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9861             }
9862             
9863             if(typeof(config.xsHeader) != 'undefined'){
9864                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9865             }
9866             
9867             if(hh.length){
9868                 c.html = hh;
9869             }
9870             
9871             if(typeof(config.tooltip) != 'undefined'){
9872                 c.tooltip = config.tooltip;
9873             }
9874             
9875             if(typeof(config.colspan) != 'undefined'){
9876                 c.colspan = config.colspan;
9877             }
9878             
9879             // hidden is handled by CSS now
9880             
9881             if(typeof(config.dataIndex) != 'undefined'){
9882                 c.sort = config.dataIndex;
9883             }
9884             
9885            
9886             
9887             if(typeof(config.align) != 'undefined' && config.align.length){
9888                 c.style += ' text-align:' + config.align + ';';
9889             }
9890             
9891             /* width is done in CSS
9892              *if(typeof(config.width) != 'undefined'){
9893                 c.style += ' width:' + config.width + 'px;';
9894                 this.totalWidth += config.width;
9895             } else {
9896                 this.totalWidth += 100; // assume minimum of 100 per column?
9897             }
9898             */
9899             
9900             if(typeof(config.cls) != 'undefined'){
9901                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9902             }
9903             // this is the bit that doesnt reall work at all...
9904             
9905             if (this.responsive) {
9906                  
9907             
9908                 ['xs','sm','md','lg'].map(function(size){
9909                     
9910                     if(typeof(config[size]) == 'undefined'){
9911                         return;
9912                     }
9913                      
9914                     if (!config[size]) { // 0 = hidden
9915                         // BS 4 '0' is treated as hide that column and below.
9916                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9917                         return;
9918                     }
9919                     
9920                     c.cls += ' col-' + size + '-' + config[size] + (
9921                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9922                     );
9923                     
9924                     
9925                 });
9926             }
9927             // at the end?
9928             
9929             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9930             
9931             
9932             
9933             
9934             header.cn.push(c)
9935         }
9936         
9937         return header;
9938     },
9939     
9940     renderBody : function()
9941     {
9942         var body = {
9943             tag: 'tbody',
9944             cn : [
9945                 {
9946                     tag: 'tr',
9947                     cn : [
9948                         {
9949                             tag : 'td',
9950                             colspan :  this.cm.getColumnCount()
9951                         }
9952                     ]
9953                 }
9954             ]
9955         };
9956         
9957         return body;
9958     },
9959     
9960     renderFooter : function()
9961     {
9962         var footer = {
9963             tag: 'tfoot',
9964             cn : [
9965                 {
9966                     tag: 'tr',
9967                     cn : [
9968                         {
9969                             tag : 'td',
9970                             colspan :  this.cm.getColumnCount()
9971                         }
9972                     ]
9973                 }
9974             ]
9975         };
9976         
9977         return footer;
9978     },
9979     
9980     
9981     
9982     onLoad : function()
9983     {
9984 //        Roo.log('ds onload');
9985         this.clear();
9986         
9987         var _this = this;
9988         var cm = this.cm;
9989         var ds = this.store;
9990         
9991         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9992             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9993             if (_this.store.sortInfo) {
9994                     
9995                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9996                     e.select('i', true).addClass(['fa-arrow-up']);
9997                 }
9998                 
9999                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10000                     e.select('i', true).addClass(['fa-arrow-down']);
10001                 }
10002             }
10003         });
10004         
10005         var tbody =  this.bodyEl;
10006               
10007         if(ds.getCount() > 0){
10008             ds.data.each(function(d,rowIndex){
10009                 var row =  this.renderRow(cm, ds, rowIndex);
10010                 
10011                 tbody.createChild(row);
10012                 
10013                 var _this = this;
10014                 
10015                 if(row.cellObjects.length){
10016                     Roo.each(row.cellObjects, function(r){
10017                         _this.renderCellObject(r);
10018                     })
10019                 }
10020                 
10021             }, this);
10022         } else if (this.empty_results.length) {
10023             this.el.mask(this.empty_results, 'no-spinner');
10024         }
10025         
10026         var tfoot = this.el.select('tfoot', true).first();
10027         
10028         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10029             
10030             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10031             
10032             var total = this.ds.getTotalCount();
10033             
10034             if(this.footer.pageSize < total){
10035                 this.mainFoot.show();
10036             }
10037         }
10038         
10039         Roo.each(this.el.select('tbody td', true).elements, function(e){
10040             e.on('mouseover', _this.onMouseover, _this);
10041         });
10042         
10043         Roo.each(this.el.select('tbody td', true).elements, function(e){
10044             e.on('mouseout', _this.onMouseout, _this);
10045         });
10046         this.fireEvent('rowsrendered', this);
10047         
10048         this.autoSize();
10049         
10050         this.initCSS(); /// resize cols
10051
10052         
10053     },
10054     
10055     
10056     onUpdate : function(ds,record)
10057     {
10058         this.refreshRow(record);
10059         this.autoSize();
10060     },
10061     
10062     onRemove : function(ds, record, index, isUpdate){
10063         if(isUpdate !== true){
10064             this.fireEvent("beforerowremoved", this, index, record);
10065         }
10066         var bt = this.bodyEl.dom;
10067         
10068         var rows = this.el.select('tbody > tr', true).elements;
10069         
10070         if(typeof(rows[index]) != 'undefined'){
10071             bt.removeChild(rows[index].dom);
10072         }
10073         
10074 //        if(bt.rows[index]){
10075 //            bt.removeChild(bt.rows[index]);
10076 //        }
10077         
10078         if(isUpdate !== true){
10079             //this.stripeRows(index);
10080             //this.syncRowHeights(index, index);
10081             //this.layout();
10082             this.fireEvent("rowremoved", this, index, record);
10083         }
10084     },
10085     
10086     onAdd : function(ds, records, rowIndex)
10087     {
10088         //Roo.log('on Add called');
10089         // - note this does not handle multiple adding very well..
10090         var bt = this.bodyEl.dom;
10091         for (var i =0 ; i < records.length;i++) {
10092             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10093             //Roo.log(records[i]);
10094             //Roo.log(this.store.getAt(rowIndex+i));
10095             this.insertRow(this.store, rowIndex + i, false);
10096             return;
10097         }
10098         
10099     },
10100     
10101     
10102     refreshRow : function(record){
10103         var ds = this.store, index;
10104         if(typeof record == 'number'){
10105             index = record;
10106             record = ds.getAt(index);
10107         }else{
10108             index = ds.indexOf(record);
10109             if (index < 0) {
10110                 return; // should not happen - but seems to 
10111             }
10112         }
10113         this.insertRow(ds, index, true);
10114         this.autoSize();
10115         this.onRemove(ds, record, index+1, true);
10116         this.autoSize();
10117         //this.syncRowHeights(index, index);
10118         //this.layout();
10119         this.fireEvent("rowupdated", this, index, record);
10120     },
10121     // private - called by RowSelection
10122     onRowSelect : function(rowIndex){
10123         var row = this.getRowDom(rowIndex);
10124         row.addClass(['bg-info','info']);
10125     },
10126     // private - called by RowSelection
10127     onRowDeselect : function(rowIndex)
10128     {
10129         if (rowIndex < 0) {
10130             return;
10131         }
10132         var row = this.getRowDom(rowIndex);
10133         row.removeClass(['bg-info','info']);
10134     },
10135       /**
10136      * Focuses the specified row.
10137      * @param {Number} row The row index
10138      */
10139     focusRow : function(row)
10140     {
10141         //Roo.log('GridView.focusRow');
10142         var x = this.bodyEl.dom.scrollLeft;
10143         this.focusCell(row, 0, false);
10144         this.bodyEl.dom.scrollLeft = x;
10145
10146     },
10147      /**
10148      * Focuses the specified cell.
10149      * @param {Number} row The row index
10150      * @param {Number} col The column index
10151      * @param {Boolean} hscroll false to disable horizontal scrolling
10152      */
10153     focusCell : function(row, col, hscroll)
10154     {
10155         //Roo.log('GridView.focusCell');
10156         var el = this.ensureVisible(row, col, hscroll);
10157         // not sure what focusEL achives = it's a <a> pos relative 
10158         //this.focusEl.alignTo(el, "tl-tl");
10159         //if(Roo.isGecko){
10160         //    this.focusEl.focus();
10161         //}else{
10162         //    this.focusEl.focus.defer(1, this.focusEl);
10163         //}
10164     },
10165     
10166      /**
10167      * Scrolls the specified cell into view
10168      * @param {Number} row The row index
10169      * @param {Number} col The column index
10170      * @param {Boolean} hscroll false to disable horizontal scrolling
10171      */
10172     ensureVisible : function(row, col, hscroll)
10173     {
10174         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10175         //return null; //disable for testing.
10176         if(typeof row != "number"){
10177             row = row.rowIndex;
10178         }
10179         if(row < 0 && row >= this.ds.getCount()){
10180             return  null;
10181         }
10182         col = (col !== undefined ? col : 0);
10183         var cm = this.cm;
10184         while(cm.isHidden(col)){
10185             col++;
10186         }
10187
10188         var el = this.getCellDom(row, col);
10189         if(!el){
10190             return null;
10191         }
10192         var c = this.bodyEl.dom;
10193
10194         var ctop = parseInt(el.offsetTop, 10);
10195         var cleft = parseInt(el.offsetLeft, 10);
10196         var cbot = ctop + el.offsetHeight;
10197         var cright = cleft + el.offsetWidth;
10198
10199         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10200         var ch = 0; //?? header is not withing the area?
10201         var stop = parseInt(c.scrollTop, 10);
10202         var sleft = parseInt(c.scrollLeft, 10);
10203         var sbot = stop + ch;
10204         var sright = sleft + c.clientWidth;
10205         /*
10206         Roo.log('GridView.ensureVisible:' +
10207                 ' ctop:' + ctop +
10208                 ' c.clientHeight:' + c.clientHeight +
10209                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10210                 ' stop:' + stop +
10211                 ' cbot:' + cbot +
10212                 ' sbot:' + sbot +
10213                 ' ch:' + ch  
10214                 );
10215         */
10216         if(ctop < stop){
10217             c.scrollTop = ctop;
10218             //Roo.log("set scrolltop to ctop DISABLE?");
10219         }else if(cbot > sbot){
10220             //Roo.log("set scrolltop to cbot-ch");
10221             c.scrollTop = cbot-ch;
10222         }
10223
10224         if(hscroll !== false){
10225             if(cleft < sleft){
10226                 c.scrollLeft = cleft;
10227             }else if(cright > sright){
10228                 c.scrollLeft = cright-c.clientWidth;
10229             }
10230         }
10231
10232         return el;
10233     },
10234     
10235     
10236     insertRow : function(dm, rowIndex, isUpdate){
10237         
10238         if(!isUpdate){
10239             this.fireEvent("beforerowsinserted", this, rowIndex);
10240         }
10241             //var s = this.getScrollState();
10242         var row = this.renderRow(this.cm, this.store, rowIndex);
10243         // insert before rowIndex..
10244         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10245         
10246         var _this = this;
10247                 
10248         if(row.cellObjects.length){
10249             Roo.each(row.cellObjects, function(r){
10250                 _this.renderCellObject(r);
10251             })
10252         }
10253             
10254         if(!isUpdate){
10255             this.fireEvent("rowsinserted", this, rowIndex);
10256             //this.syncRowHeights(firstRow, lastRow);
10257             //this.stripeRows(firstRow);
10258             //this.layout();
10259         }
10260         
10261     },
10262     
10263     
10264     getRowDom : function(rowIndex)
10265     {
10266         var rows = this.el.select('tbody > tr', true).elements;
10267         
10268         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10269         
10270     },
10271     getCellDom : function(rowIndex, colIndex)
10272     {
10273         var row = this.getRowDom(rowIndex);
10274         if (row === false) {
10275             return false;
10276         }
10277         var cols = row.select('td', true).elements;
10278         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10279         
10280     },
10281     
10282     // returns the object tree for a tr..
10283   
10284     
10285     renderRow : function(cm, ds, rowIndex) 
10286     {
10287         var d = ds.getAt(rowIndex);
10288         
10289         var row = {
10290             tag : 'tr',
10291             cls : 'x-row-' + rowIndex,
10292             cn : []
10293         };
10294             
10295         var cellObjects = [];
10296         
10297         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10298             var config = cm.config[i];
10299             
10300             var renderer = cm.getRenderer(i);
10301             var value = '';
10302             var id = false;
10303             
10304             if(typeof(renderer) !== 'undefined'){
10305                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10306             }
10307             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10308             // and are rendered into the cells after the row is rendered - using the id for the element.
10309             
10310             if(typeof(value) === 'object'){
10311                 id = Roo.id();
10312                 cellObjects.push({
10313                     container : id,
10314                     cfg : value 
10315                 })
10316             }
10317             
10318             var rowcfg = {
10319                 record: d,
10320                 rowIndex : rowIndex,
10321                 colIndex : i,
10322                 rowClass : ''
10323             };
10324
10325             this.fireEvent('rowclass', this, rowcfg);
10326             
10327             var td = {
10328                 tag: 'td',
10329                 // this might end up displaying HTML?
10330                 // this is too messy... - better to only do it on columsn you know are going to be too long
10331                 //tooltip : (typeof(value) === 'object') ? '' : value,
10332                 cls : rowcfg.rowClass + ' x-col-' + i,
10333                 style: '',
10334                 html: (typeof(value) === 'object') ? '' : value
10335             };
10336             
10337             if (id) {
10338                 td.id = id;
10339             }
10340             
10341             if(typeof(config.colspan) != 'undefined'){
10342                 td.colspan = config.colspan;
10343             }
10344             
10345             
10346             
10347             if(typeof(config.align) != 'undefined' && config.align.length){
10348                 td.style += ' text-align:' + config.align + ';';
10349             }
10350             if(typeof(config.valign) != 'undefined' && config.valign.length){
10351                 td.style += ' vertical-align:' + config.valign + ';';
10352             }
10353             /*
10354             if(typeof(config.width) != 'undefined'){
10355                 td.style += ' width:' +  config.width + 'px;';
10356             }
10357             */
10358             
10359             if(typeof(config.cursor) != 'undefined'){
10360                 td.style += ' cursor:' +  config.cursor + ';';
10361             }
10362             
10363             if(typeof(config.cls) != 'undefined'){
10364                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10365             }
10366             if (this.responsive) {
10367                 ['xs','sm','md','lg'].map(function(size){
10368                     
10369                     if(typeof(config[size]) == 'undefined'){
10370                         return;
10371                     }
10372                     
10373                     
10374                       
10375                     if (!config[size]) { // 0 = hidden
10376                         // BS 4 '0' is treated as hide that column and below.
10377                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10378                         return;
10379                     }
10380                     
10381                     td.cls += ' col-' + size + '-' + config[size] + (
10382                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10383                     );
10384                      
10385     
10386                 });
10387             }
10388             row.cn.push(td);
10389            
10390         }
10391         
10392         row.cellObjects = cellObjects;
10393         
10394         return row;
10395           
10396     },
10397     
10398     
10399     
10400     onBeforeLoad : function()
10401     {
10402         this.el.unmask(); // if needed.
10403     },
10404      /**
10405      * Remove all rows
10406      */
10407     clear : function()
10408     {
10409         this.el.select('tbody', true).first().dom.innerHTML = '';
10410     },
10411     /**
10412      * Show or hide a row.
10413      * @param {Number} rowIndex to show or hide
10414      * @param {Boolean} state hide
10415      */
10416     setRowVisibility : function(rowIndex, state)
10417     {
10418         var bt = this.bodyEl.dom;
10419         
10420         var rows = this.el.select('tbody > tr', true).elements;
10421         
10422         if(typeof(rows[rowIndex]) == 'undefined'){
10423             return;
10424         }
10425         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10426         
10427     },
10428     
10429     
10430     getSelectionModel : function(){
10431         if(!this.selModel){
10432             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10433         }
10434         return this.selModel;
10435     },
10436     /*
10437      * Render the Roo.bootstrap object from renderder
10438      */
10439     renderCellObject : function(r)
10440     {
10441         var _this = this;
10442         
10443         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10444         
10445         var t = r.cfg.render(r.container);
10446         
10447         if(r.cfg.cn){
10448             Roo.each(r.cfg.cn, function(c){
10449                 var child = {
10450                     container: t.getChildContainer(),
10451                     cfg: c
10452                 };
10453                 _this.renderCellObject(child);
10454             })
10455         }
10456     },
10457     /**
10458      * get the Row Index from a dom element.
10459      * @param {Roo.Element} row The row to look for
10460      * @returns {Number} the row
10461      */
10462     getRowIndex : function(row)
10463     {
10464         var rowIndex = -1;
10465         
10466         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10467             if(el != row){
10468                 return;
10469             }
10470             
10471             rowIndex = index;
10472         });
10473         
10474         return rowIndex;
10475     },
10476     /**
10477      * get the header TH element for columnIndex
10478      * @param {Number} columnIndex
10479      * @returns {Roo.Element}
10480      */
10481     getHeaderIndex: function(colIndex)
10482     {
10483         var cols = this.headEl.select('th', true).elements;
10484         return cols[colIndex]; 
10485     },
10486     /**
10487      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10488      * @param {domElement} cell to look for
10489      * @returns {Number} the column
10490      */
10491     getCellIndex : function(cell)
10492     {
10493         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10494         if(id){
10495             return parseInt(id[1], 10);
10496         }
10497         return 0;
10498     },
10499      /**
10500      * Returns the grid's underlying element = used by panel.Grid
10501      * @return {Element} The element
10502      */
10503     getGridEl : function(){
10504         return this.el;
10505     },
10506      /**
10507      * Forces a resize - used by panel.Grid
10508      * @return {Element} The element
10509      */
10510     autoSize : function()
10511     {
10512         //var ctr = Roo.get(this.container.dom.parentElement);
10513         var ctr = Roo.get(this.el.dom);
10514         
10515         var thd = this.getGridEl().select('thead',true).first();
10516         var tbd = this.getGridEl().select('tbody', true).first();
10517         var tfd = this.getGridEl().select('tfoot', true).first();
10518         
10519         var cw = ctr.getWidth();
10520         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10521         
10522         if (tbd) {
10523             
10524             tbd.setWidth(ctr.getWidth());
10525             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10526             // this needs fixing for various usage - currently only hydra job advers I think..
10527             //tdb.setHeight(
10528             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10529             //); 
10530             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10531             cw -= barsize;
10532         }
10533         cw = Math.max(cw, this.totalWidth);
10534         this.getGridEl().select('tbody tr',true).setWidth(cw);
10535         this.initCSS();
10536         
10537         // resize 'expandable coloumn?
10538         
10539         return; // we doe not have a view in this design..
10540         
10541     },
10542     onBodyScroll: function()
10543     {
10544         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10545         if(this.headEl){
10546             this.headEl.setStyle({
10547                 'position' : 'relative',
10548                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10549             });
10550         }
10551         
10552         if(this.lazyLoad){
10553             
10554             var scrollHeight = this.bodyEl.dom.scrollHeight;
10555             
10556             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10557             
10558             var height = this.bodyEl.getHeight();
10559             
10560             if(scrollHeight - height == scrollTop) {
10561                 
10562                 var total = this.ds.getTotalCount();
10563                 
10564                 if(this.footer.cursor + this.footer.pageSize < total){
10565                     
10566                     this.footer.ds.load({
10567                         params : {
10568                             start : this.footer.cursor + this.footer.pageSize,
10569                             limit : this.footer.pageSize
10570                         },
10571                         add : true
10572                     });
10573                 }
10574             }
10575             
10576         }
10577     },
10578     onColumnSplitterMoved : function(i, diff)
10579     {
10580         this.userResized = true;
10581         
10582         var cm = this.colModel;
10583         
10584         var w = this.getHeaderIndex(i).getWidth() + diff;
10585         
10586         
10587         cm.setColumnWidth(i, w, true);
10588         this.initCSS();
10589         //var cid = cm.getColumnId(i); << not used in this version?
10590        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10591         
10592         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10593         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10594         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10595 */
10596         //this.updateSplitters();
10597         //this.layout(); << ??
10598         this.fireEvent("columnresize", i, w);
10599     },
10600     onHeaderChange : function()
10601     {
10602         var header = this.renderHeader();
10603         var table = this.el.select('table', true).first();
10604         
10605         this.headEl.remove();
10606         this.headEl = table.createChild(header, this.bodyEl, false);
10607         
10608         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10609             e.on('click', this.sort, this);
10610         }, this);
10611         
10612         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10613             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10614         }
10615         
10616     },
10617     
10618     onHiddenChange : function(colModel, colIndex, hidden)
10619     {
10620         /*
10621         this.cm.setHidden()
10622         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10623         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10624         
10625         this.CSS.updateRule(thSelector, "display", "");
10626         this.CSS.updateRule(tdSelector, "display", "");
10627         
10628         if(hidden){
10629             this.CSS.updateRule(thSelector, "display", "none");
10630             this.CSS.updateRule(tdSelector, "display", "none");
10631         }
10632         */
10633         // onload calls initCSS()
10634         this.onHeaderChange();
10635         this.onLoad();
10636     },
10637     
10638     setColumnWidth: function(col_index, width)
10639     {
10640         // width = "md-2 xs-2..."
10641         if(!this.colModel.config[col_index]) {
10642             return;
10643         }
10644         
10645         var w = width.split(" ");
10646         
10647         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10648         
10649         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10650         
10651         
10652         for(var j = 0; j < w.length; j++) {
10653             
10654             if(!w[j]) {
10655                 continue;
10656             }
10657             
10658             var size_cls = w[j].split("-");
10659             
10660             if(!Number.isInteger(size_cls[1] * 1)) {
10661                 continue;
10662             }
10663             
10664             if(!this.colModel.config[col_index][size_cls[0]]) {
10665                 continue;
10666             }
10667             
10668             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10669                 continue;
10670             }
10671             
10672             h_row[0].classList.replace(
10673                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10674                 "col-"+size_cls[0]+"-"+size_cls[1]
10675             );
10676             
10677             for(var i = 0; i < rows.length; i++) {
10678                 
10679                 var size_cls = w[j].split("-");
10680                 
10681                 if(!Number.isInteger(size_cls[1] * 1)) {
10682                     continue;
10683                 }
10684                 
10685                 if(!this.colModel.config[col_index][size_cls[0]]) {
10686                     continue;
10687                 }
10688                 
10689                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10690                     continue;
10691                 }
10692                 
10693                 rows[i].classList.replace(
10694                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10695                     "col-"+size_cls[0]+"-"+size_cls[1]
10696                 );
10697             }
10698             
10699             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10700         }
10701     }
10702 });
10703
10704 // currently only used to find the split on drag.. 
10705 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10706
10707 /**
10708  * @depricated
10709 */
10710 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10711 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10712 /*
10713  * - LGPL
10714  *
10715  * table cell
10716  * 
10717  */
10718
10719 /**
10720  * @class Roo.bootstrap.TableCell
10721  * @extends Roo.bootstrap.Component
10722  * @children Roo.bootstrap.Component
10723  * @parent Roo.bootstrap.TableRow
10724  * Bootstrap TableCell class
10725  * 
10726  * @cfg {String} html cell contain text
10727  * @cfg {String} cls cell class
10728  * @cfg {String} tag cell tag (td|th) default td
10729  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10730  * @cfg {String} align Aligns the content in a cell
10731  * @cfg {String} axis Categorizes cells
10732  * @cfg {String} bgcolor Specifies the background color of a cell
10733  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10734  * @cfg {Number} colspan Specifies the number of columns a cell should span
10735  * @cfg {String} headers Specifies one or more header cells a cell is related to
10736  * @cfg {Number} height Sets the height of a cell
10737  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10738  * @cfg {Number} rowspan Sets the number of rows a cell should span
10739  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10740  * @cfg {String} valign Vertical aligns the content in a cell
10741  * @cfg {Number} width Specifies the width of a cell
10742  * 
10743  * @constructor
10744  * Create a new TableCell
10745  * @param {Object} config The config object
10746  */
10747
10748 Roo.bootstrap.TableCell = function(config){
10749     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10750 };
10751
10752 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10753     
10754     html: false,
10755     cls: false,
10756     tag: false,
10757     abbr: false,
10758     align: false,
10759     axis: false,
10760     bgcolor: false,
10761     charoff: false,
10762     colspan: false,
10763     headers: false,
10764     height: false,
10765     nowrap: false,
10766     rowspan: false,
10767     scope: false,
10768     valign: false,
10769     width: false,
10770     
10771     
10772     getAutoCreate : function(){
10773         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10774         
10775         cfg = {
10776             tag: 'td'
10777         };
10778         
10779         if(this.tag){
10780             cfg.tag = this.tag;
10781         }
10782         
10783         if (this.html) {
10784             cfg.html=this.html
10785         }
10786         if (this.cls) {
10787             cfg.cls=this.cls
10788         }
10789         if (this.abbr) {
10790             cfg.abbr=this.abbr
10791         }
10792         if (this.align) {
10793             cfg.align=this.align
10794         }
10795         if (this.axis) {
10796             cfg.axis=this.axis
10797         }
10798         if (this.bgcolor) {
10799             cfg.bgcolor=this.bgcolor
10800         }
10801         if (this.charoff) {
10802             cfg.charoff=this.charoff
10803         }
10804         if (this.colspan) {
10805             cfg.colspan=this.colspan
10806         }
10807         if (this.headers) {
10808             cfg.headers=this.headers
10809         }
10810         if (this.height) {
10811             cfg.height=this.height
10812         }
10813         if (this.nowrap) {
10814             cfg.nowrap=this.nowrap
10815         }
10816         if (this.rowspan) {
10817             cfg.rowspan=this.rowspan
10818         }
10819         if (this.scope) {
10820             cfg.scope=this.scope
10821         }
10822         if (this.valign) {
10823             cfg.valign=this.valign
10824         }
10825         if (this.width) {
10826             cfg.width=this.width
10827         }
10828         
10829         
10830         return cfg;
10831     }
10832    
10833 });
10834
10835  
10836
10837  /*
10838  * - LGPL
10839  *
10840  * table row
10841  * 
10842  */
10843
10844 /**
10845  * @class Roo.bootstrap.TableRow
10846  * @extends Roo.bootstrap.Component
10847  * @children Roo.bootstrap.TableCell
10848  * @parent Roo.bootstrap.TableBody
10849  * Bootstrap TableRow class
10850  * @cfg {String} cls row class
10851  * @cfg {String} align Aligns the content in a table row
10852  * @cfg {String} bgcolor Specifies a background color for a table row
10853  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10854  * @cfg {String} valign Vertical aligns the content in a table row
10855  * 
10856  * @constructor
10857  * Create a new TableRow
10858  * @param {Object} config The config object
10859  */
10860
10861 Roo.bootstrap.TableRow = function(config){
10862     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10863 };
10864
10865 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10866     
10867     cls: false,
10868     align: false,
10869     bgcolor: false,
10870     charoff: false,
10871     valign: false,
10872     
10873     getAutoCreate : function(){
10874         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10875         
10876         cfg = {
10877             tag: 'tr'
10878         };
10879             
10880         if(this.cls){
10881             cfg.cls = this.cls;
10882         }
10883         if(this.align){
10884             cfg.align = this.align;
10885         }
10886         if(this.bgcolor){
10887             cfg.bgcolor = this.bgcolor;
10888         }
10889         if(this.charoff){
10890             cfg.charoff = this.charoff;
10891         }
10892         if(this.valign){
10893             cfg.valign = this.valign;
10894         }
10895         
10896         return cfg;
10897     }
10898    
10899 });
10900
10901  
10902
10903  /*
10904  * - LGPL
10905  *
10906  * table body
10907  * 
10908  */
10909
10910 /**
10911  * @class Roo.bootstrap.TableBody
10912  * @extends Roo.bootstrap.Component
10913  * @children Roo.bootstrap.TableRow
10914  * @parent Roo.bootstrap.Table
10915  * Bootstrap TableBody class
10916  * @cfg {String} cls element class
10917  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10918  * @cfg {String} align Aligns the content inside the element
10919  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10920  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10921  * 
10922  * @constructor
10923  * Create a new TableBody
10924  * @param {Object} config The config object
10925  */
10926
10927 Roo.bootstrap.TableBody = function(config){
10928     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10929 };
10930
10931 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10932     
10933     cls: false,
10934     tag: false,
10935     align: false,
10936     charoff: false,
10937     valign: false,
10938     
10939     getAutoCreate : function(){
10940         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10941         
10942         cfg = {
10943             tag: 'tbody'
10944         };
10945             
10946         if (this.cls) {
10947             cfg.cls=this.cls
10948         }
10949         if(this.tag){
10950             cfg.tag = this.tag;
10951         }
10952         
10953         if(this.align){
10954             cfg.align = this.align;
10955         }
10956         if(this.charoff){
10957             cfg.charoff = this.charoff;
10958         }
10959         if(this.valign){
10960             cfg.valign = this.valign;
10961         }
10962         
10963         return cfg;
10964     }
10965     
10966     
10967 //    initEvents : function()
10968 //    {
10969 //        
10970 //        if(!this.store){
10971 //            return;
10972 //        }
10973 //        
10974 //        this.store = Roo.factory(this.store, Roo.data);
10975 //        this.store.on('load', this.onLoad, this);
10976 //        
10977 //        this.store.load();
10978 //        
10979 //    },
10980 //    
10981 //    onLoad: function () 
10982 //    {   
10983 //        this.fireEvent('load', this);
10984 //    }
10985 //    
10986 //   
10987 });
10988
10989  
10990
10991  /*
10992  * Based on:
10993  * Ext JS Library 1.1.1
10994  * Copyright(c) 2006-2007, Ext JS, LLC.
10995  *
10996  * Originally Released Under LGPL - original licence link has changed is not relivant.
10997  *
10998  * Fork - LGPL
10999  * <script type="text/javascript">
11000  */
11001
11002 // as we use this in bootstrap.
11003 Roo.namespace('Roo.form');
11004  /**
11005  * @class Roo.form.Action
11006  * Internal Class used to handle form actions
11007  * @constructor
11008  * @param {Roo.form.BasicForm} el The form element or its id
11009  * @param {Object} config Configuration options
11010  */
11011
11012  
11013  
11014 // define the action interface
11015 Roo.form.Action = function(form, options){
11016     this.form = form;
11017     this.options = options || {};
11018 };
11019 /**
11020  * Client Validation Failed
11021  * @const 
11022  */
11023 Roo.form.Action.CLIENT_INVALID = 'client';
11024 /**
11025  * Server Validation Failed
11026  * @const 
11027  */
11028 Roo.form.Action.SERVER_INVALID = 'server';
11029  /**
11030  * Connect to Server Failed
11031  * @const 
11032  */
11033 Roo.form.Action.CONNECT_FAILURE = 'connect';
11034 /**
11035  * Reading Data from Server Failed
11036  * @const 
11037  */
11038 Roo.form.Action.LOAD_FAILURE = 'load';
11039
11040 Roo.form.Action.prototype = {
11041     type : 'default',
11042     failureType : undefined,
11043     response : undefined,
11044     result : undefined,
11045
11046     // interface method
11047     run : function(options){
11048
11049     },
11050
11051     // interface method
11052     success : function(response){
11053
11054     },
11055
11056     // interface method
11057     handleResponse : function(response){
11058
11059     },
11060
11061     // default connection failure
11062     failure : function(response){
11063         
11064         this.response = response;
11065         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11066         this.form.afterAction(this, false);
11067     },
11068
11069     processResponse : function(response){
11070         this.response = response;
11071         if(!response.responseText){
11072             return true;
11073         }
11074         this.result = this.handleResponse(response);
11075         return this.result;
11076     },
11077
11078     // utility functions used internally
11079     getUrl : function(appendParams){
11080         var url = this.options.url || this.form.url || this.form.el.dom.action;
11081         if(appendParams){
11082             var p = this.getParams();
11083             if(p){
11084                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11085             }
11086         }
11087         return url;
11088     },
11089
11090     getMethod : function(){
11091         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11092     },
11093
11094     getParams : function(){
11095         var bp = this.form.baseParams;
11096         var p = this.options.params;
11097         if(p){
11098             if(typeof p == "object"){
11099                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11100             }else if(typeof p == 'string' && bp){
11101                 p += '&' + Roo.urlEncode(bp);
11102             }
11103         }else if(bp){
11104             p = Roo.urlEncode(bp);
11105         }
11106         return p;
11107     },
11108
11109     createCallback : function(){
11110         return {
11111             success: this.success,
11112             failure: this.failure,
11113             scope: this,
11114             timeout: (this.form.timeout*1000),
11115             upload: this.form.fileUpload ? this.success : undefined
11116         };
11117     }
11118 };
11119
11120 Roo.form.Action.Submit = function(form, options){
11121     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11122 };
11123
11124 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11125     type : 'submit',
11126
11127     haveProgress : false,
11128     uploadComplete : false,
11129     
11130     // uploadProgress indicator.
11131     uploadProgress : function()
11132     {
11133         if (!this.form.progressUrl) {
11134             return;
11135         }
11136         
11137         if (!this.haveProgress) {
11138             Roo.MessageBox.progress("Uploading", "Uploading");
11139         }
11140         if (this.uploadComplete) {
11141            Roo.MessageBox.hide();
11142            return;
11143         }
11144         
11145         this.haveProgress = true;
11146    
11147         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11148         
11149         var c = new Roo.data.Connection();
11150         c.request({
11151             url : this.form.progressUrl,
11152             params: {
11153                 id : uid
11154             },
11155             method: 'GET',
11156             success : function(req){
11157                //console.log(data);
11158                 var rdata = false;
11159                 var edata;
11160                 try  {
11161                    rdata = Roo.decode(req.responseText)
11162                 } catch (e) {
11163                     Roo.log("Invalid data from server..");
11164                     Roo.log(edata);
11165                     return;
11166                 }
11167                 if (!rdata || !rdata.success) {
11168                     Roo.log(rdata);
11169                     Roo.MessageBox.alert(Roo.encode(rdata));
11170                     return;
11171                 }
11172                 var data = rdata.data;
11173                 
11174                 if (this.uploadComplete) {
11175                    Roo.MessageBox.hide();
11176                    return;
11177                 }
11178                    
11179                 if (data){
11180                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11181                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11182                     );
11183                 }
11184                 this.uploadProgress.defer(2000,this);
11185             },
11186        
11187             failure: function(data) {
11188                 Roo.log('progress url failed ');
11189                 Roo.log(data);
11190             },
11191             scope : this
11192         });
11193            
11194     },
11195     
11196     
11197     run : function()
11198     {
11199         // run get Values on the form, so it syncs any secondary forms.
11200         this.form.getValues();
11201         
11202         var o = this.options;
11203         var method = this.getMethod();
11204         var isPost = method == 'POST';
11205         if(o.clientValidation === false || this.form.isValid()){
11206             
11207             if (this.form.progressUrl) {
11208                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11209                     (new Date() * 1) + '' + Math.random());
11210                     
11211             } 
11212             
11213             
11214             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11215                 form:this.form.el.dom,
11216                 url:this.getUrl(!isPost),
11217                 method: method,
11218                 params:isPost ? this.getParams() : null,
11219                 isUpload: this.form.fileUpload,
11220                 formData : this.form.formData
11221             }));
11222             
11223             this.uploadProgress();
11224
11225         }else if (o.clientValidation !== false){ // client validation failed
11226             this.failureType = Roo.form.Action.CLIENT_INVALID;
11227             this.form.afterAction(this, false);
11228         }
11229     },
11230
11231     success : function(response)
11232     {
11233         this.uploadComplete= true;
11234         if (this.haveProgress) {
11235             Roo.MessageBox.hide();
11236         }
11237         
11238         
11239         var result = this.processResponse(response);
11240         if(result === true || result.success){
11241             this.form.afterAction(this, true);
11242             return;
11243         }
11244         if(result.errors){
11245             this.form.markInvalid(result.errors);
11246             this.failureType = Roo.form.Action.SERVER_INVALID;
11247         }
11248         this.form.afterAction(this, false);
11249     },
11250     failure : function(response)
11251     {
11252         this.uploadComplete= true;
11253         if (this.haveProgress) {
11254             Roo.MessageBox.hide();
11255         }
11256         
11257         this.response = response;
11258         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11259         this.form.afterAction(this, false);
11260     },
11261     
11262     handleResponse : function(response){
11263         if(this.form.errorReader){
11264             var rs = this.form.errorReader.read(response);
11265             var errors = [];
11266             if(rs.records){
11267                 for(var i = 0, len = rs.records.length; i < len; i++) {
11268                     var r = rs.records[i];
11269                     errors[i] = r.data;
11270                 }
11271             }
11272             if(errors.length < 1){
11273                 errors = null;
11274             }
11275             return {
11276                 success : rs.success,
11277                 errors : errors
11278             };
11279         }
11280         var ret = false;
11281         try {
11282             ret = Roo.decode(response.responseText);
11283         } catch (e) {
11284             ret = {
11285                 success: false,
11286                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11287                 errors : []
11288             };
11289         }
11290         return ret;
11291         
11292     }
11293 });
11294
11295
11296 Roo.form.Action.Load = function(form, options){
11297     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11298     this.reader = this.form.reader;
11299 };
11300
11301 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11302     type : 'load',
11303
11304     run : function(){
11305         
11306         Roo.Ajax.request(Roo.apply(
11307                 this.createCallback(), {
11308                     method:this.getMethod(),
11309                     url:this.getUrl(false),
11310                     params:this.getParams()
11311         }));
11312     },
11313
11314     success : function(response){
11315         
11316         var result = this.processResponse(response);
11317         if(result === true || !result.success || !result.data){
11318             this.failureType = Roo.form.Action.LOAD_FAILURE;
11319             this.form.afterAction(this, false);
11320             return;
11321         }
11322         this.form.clearInvalid();
11323         this.form.setValues(result.data);
11324         this.form.afterAction(this, true);
11325     },
11326
11327     handleResponse : function(response){
11328         if(this.form.reader){
11329             var rs = this.form.reader.read(response);
11330             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11331             return {
11332                 success : rs.success,
11333                 data : data
11334             };
11335         }
11336         return Roo.decode(response.responseText);
11337     }
11338 });
11339
11340 Roo.form.Action.ACTION_TYPES = {
11341     'load' : Roo.form.Action.Load,
11342     'submit' : Roo.form.Action.Submit
11343 };/*
11344  * - LGPL
11345  *
11346  * form
11347  *
11348  */
11349
11350 /**
11351  * @class Roo.bootstrap.form.Form
11352  * @extends Roo.bootstrap.Component
11353  * @children Roo.bootstrap.Component
11354  * Bootstrap Form class
11355  * @cfg {String} method  GET | POST (default POST)
11356  * @cfg {String} labelAlign top | left (default top)
11357  * @cfg {String} align left  | right - for navbars
11358  * @cfg {Boolean} loadMask load mask when submit (default true)
11359
11360  *
11361  * @constructor
11362  * Create a new Form
11363  * @param {Object} config The config object
11364  */
11365
11366
11367 Roo.bootstrap.form.Form = function(config){
11368     
11369     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11370     
11371     Roo.bootstrap.form.Form.popover.apply();
11372     
11373     this.addEvents({
11374         /**
11375          * @event clientvalidation
11376          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11377          * @param {Form} this
11378          * @param {Boolean} valid true if the form has passed client-side validation
11379          */
11380         clientvalidation: true,
11381         /**
11382          * @event beforeaction
11383          * Fires before any action is performed. Return false to cancel the action.
11384          * @param {Form} this
11385          * @param {Action} action The action to be performed
11386          */
11387         beforeaction: true,
11388         /**
11389          * @event actionfailed
11390          * Fires when an action fails.
11391          * @param {Form} this
11392          * @param {Action} action The action that failed
11393          */
11394         actionfailed : true,
11395         /**
11396          * @event actioncomplete
11397          * Fires when an action is completed.
11398          * @param {Form} this
11399          * @param {Action} action The action that completed
11400          */
11401         actioncomplete : true
11402     });
11403 };
11404
11405 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11406
11407      /**
11408      * @cfg {String} method
11409      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11410      */
11411     method : 'POST',
11412     /**
11413      * @cfg {String} url
11414      * The URL to use for form actions if one isn't supplied in the action options.
11415      */
11416     /**
11417      * @cfg {Boolean} fileUpload
11418      * Set to true if this form is a file upload.
11419      */
11420
11421     /**
11422      * @cfg {Object} baseParams
11423      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11424      */
11425
11426     /**
11427      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11428      */
11429     timeout: 30,
11430     /**
11431      * @cfg {Sting} align (left|right) for navbar forms
11432      */
11433     align : 'left',
11434
11435     // private
11436     activeAction : null,
11437
11438     /**
11439      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11440      * element by passing it or its id or mask the form itself by passing in true.
11441      * @type Mixed
11442      */
11443     waitMsgTarget : false,
11444
11445     loadMask : true,
11446     
11447     /**
11448      * @cfg {Boolean} errorMask (true|false) default false
11449      */
11450     errorMask : false,
11451     
11452     /**
11453      * @cfg {Number} maskOffset Default 100
11454      */
11455     maskOffset : 100,
11456     
11457     /**
11458      * @cfg {Boolean} maskBody
11459      */
11460     maskBody : false,
11461
11462     getAutoCreate : function(){
11463
11464         var cfg = {
11465             tag: 'form',
11466             method : this.method || 'POST',
11467             id : this.id || Roo.id(),
11468             cls : ''
11469         };
11470         if (this.parent().xtype.match(/^Nav/)) {
11471             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11472
11473         }
11474
11475         if (this.labelAlign == 'left' ) {
11476             cfg.cls += ' form-horizontal';
11477         }
11478
11479
11480         return cfg;
11481     },
11482     initEvents : function()
11483     {
11484         this.el.on('submit', this.onSubmit, this);
11485         // this was added as random key presses on the form where triggering form submit.
11486         this.el.on('keypress', function(e) {
11487             if (e.getCharCode() != 13) {
11488                 return true;
11489             }
11490             // we might need to allow it for textareas.. and some other items.
11491             // check e.getTarget().
11492
11493             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11494                 return true;
11495             }
11496
11497             Roo.log("keypress blocked");
11498
11499             e.preventDefault();
11500             return false;
11501         });
11502         
11503     },
11504     // private
11505     onSubmit : function(e){
11506         e.stopEvent();
11507     },
11508
11509      /**
11510      * Returns true if client-side validation on the form is successful.
11511      * @return Boolean
11512      */
11513     isValid : function(){
11514         var items = this.getItems();
11515         var valid = true;
11516         var target = false;
11517         
11518         items.each(function(f){
11519             
11520             if(f.validate()){
11521                 return;
11522             }
11523             
11524             Roo.log('invalid field: ' + f.name);
11525             
11526             valid = false;
11527
11528             if(!target && f.el.isVisible(true)){
11529                 target = f;
11530             }
11531            
11532         });
11533         
11534         if(this.errorMask && !valid){
11535             Roo.bootstrap.form.Form.popover.mask(this, target);
11536         }
11537         
11538         return valid;
11539     },
11540     
11541     /**
11542      * Returns true if any fields in this form have changed since their original load.
11543      * @return Boolean
11544      */
11545     isDirty : function(){
11546         var dirty = false;
11547         var items = this.getItems();
11548         items.each(function(f){
11549            if(f.isDirty()){
11550                dirty = true;
11551                return false;
11552            }
11553            return true;
11554         });
11555         return dirty;
11556     },
11557      /**
11558      * Performs a predefined action (submit or load) or custom actions you define on this form.
11559      * @param {String} actionName The name of the action type
11560      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11561      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11562      * accept other config options):
11563      * <pre>
11564 Property          Type             Description
11565 ----------------  ---------------  ----------------------------------------------------------------------------------
11566 url               String           The url for the action (defaults to the form's url)
11567 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11568 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11569 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11570                                    validate the form on the client (defaults to false)
11571      * </pre>
11572      * @return {BasicForm} this
11573      */
11574     doAction : function(action, options){
11575         if(typeof action == 'string'){
11576             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11577         }
11578         if(this.fireEvent('beforeaction', this, action) !== false){
11579             this.beforeAction(action);
11580             action.run.defer(100, action);
11581         }
11582         return this;
11583     },
11584
11585     // private
11586     beforeAction : function(action){
11587         var o = action.options;
11588         
11589         if(this.loadMask){
11590             
11591             if(this.maskBody){
11592                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11593             } else {
11594                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11595             }
11596         }
11597         // not really supported yet.. ??
11598
11599         //if(this.waitMsgTarget === true){
11600         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11601         //}else if(this.waitMsgTarget){
11602         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11603         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11604         //}else {
11605         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11606        // }
11607
11608     },
11609
11610     // private
11611     afterAction : function(action, success){
11612         this.activeAction = null;
11613         var o = action.options;
11614
11615         if(this.loadMask){
11616             
11617             if(this.maskBody){
11618                 Roo.get(document.body).unmask();
11619             } else {
11620                 this.el.unmask();
11621             }
11622         }
11623         
11624         //if(this.waitMsgTarget === true){
11625 //            this.el.unmask();
11626         //}else if(this.waitMsgTarget){
11627         //    this.waitMsgTarget.unmask();
11628         //}else{
11629         //    Roo.MessageBox.updateProgress(1);
11630         //    Roo.MessageBox.hide();
11631        // }
11632         //
11633         if(success){
11634             if(o.reset){
11635                 this.reset();
11636             }
11637             Roo.callback(o.success, o.scope, [this, action]);
11638             this.fireEvent('actioncomplete', this, action);
11639
11640         }else{
11641
11642             // failure condition..
11643             // we have a scenario where updates need confirming.
11644             // eg. if a locking scenario exists..
11645             // we look for { errors : { needs_confirm : true }} in the response.
11646             if (
11647                 (typeof(action.result) != 'undefined')  &&
11648                 (typeof(action.result.errors) != 'undefined')  &&
11649                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11650            ){
11651                 var _t = this;
11652                 Roo.log("not supported yet");
11653                  /*
11654
11655                 Roo.MessageBox.confirm(
11656                     "Change requires confirmation",
11657                     action.result.errorMsg,
11658                     function(r) {
11659                         if (r != 'yes') {
11660                             return;
11661                         }
11662                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11663                     }
11664
11665                 );
11666                 */
11667
11668
11669                 return;
11670             }
11671
11672             Roo.callback(o.failure, o.scope, [this, action]);
11673             // show an error message if no failed handler is set..
11674             if (!this.hasListener('actionfailed')) {
11675                 Roo.log("need to add dialog support");
11676                 /*
11677                 Roo.MessageBox.alert("Error",
11678                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11679                         action.result.errorMsg :
11680                         "Saving Failed, please check your entries or try again"
11681                 );
11682                 */
11683             }
11684
11685             this.fireEvent('actionfailed', this, action);
11686         }
11687
11688     },
11689     /**
11690      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11691      * @param {String} id The value to search for
11692      * @return Field
11693      */
11694     findField : function(id){
11695         var items = this.getItems();
11696         var field = items.get(id);
11697         if(!field){
11698              items.each(function(f){
11699                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11700                     field = f;
11701                     return false;
11702                 }
11703                 return true;
11704             });
11705         }
11706         return field || null;
11707     },
11708      /**
11709      * Mark fields in this form invalid in bulk.
11710      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11711      * @return {BasicForm} this
11712      */
11713     markInvalid : function(errors){
11714         if(errors instanceof Array){
11715             for(var i = 0, len = errors.length; i < len; i++){
11716                 var fieldError = errors[i];
11717                 var f = this.findField(fieldError.id);
11718                 if(f){
11719                     f.markInvalid(fieldError.msg);
11720                 }
11721             }
11722         }else{
11723             var field, id;
11724             for(id in errors){
11725                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11726                     field.markInvalid(errors[id]);
11727                 }
11728             }
11729         }
11730         //Roo.each(this.childForms || [], function (f) {
11731         //    f.markInvalid(errors);
11732         //});
11733
11734         return this;
11735     },
11736
11737     /**
11738      * Set values for fields in this form in bulk.
11739      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11740      * @return {BasicForm} this
11741      */
11742     setValues : function(values){
11743         if(values instanceof Array){ // array of objects
11744             for(var i = 0, len = values.length; i < len; i++){
11745                 var v = values[i];
11746                 var f = this.findField(v.id);
11747                 if(f){
11748                     f.setValue(v.value);
11749                     if(this.trackResetOnLoad){
11750                         f.originalValue = f.getValue();
11751                     }
11752                 }
11753             }
11754         }else{ // object hash
11755             var field, id;
11756             for(id in values){
11757                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11758
11759                     if (field.setFromData &&
11760                         field.valueField &&
11761                         field.displayField &&
11762                         // combos' with local stores can
11763                         // be queried via setValue()
11764                         // to set their value..
11765                         (field.store && !field.store.isLocal)
11766                         ) {
11767                         // it's a combo
11768                         var sd = { };
11769                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11770                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11771                         field.setFromData(sd);
11772
11773                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11774                         
11775                         field.setFromData(values);
11776                         
11777                     } else {
11778                         field.setValue(values[id]);
11779                     }
11780
11781
11782                     if(this.trackResetOnLoad){
11783                         field.originalValue = field.getValue();
11784                     }
11785                 }
11786             }
11787         }
11788
11789         //Roo.each(this.childForms || [], function (f) {
11790         //    f.setValues(values);
11791         //});
11792
11793         return this;
11794     },
11795
11796     /**
11797      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11798      * they are returned as an array.
11799      * @param {Boolean} asString
11800      * @return {Object}
11801      */
11802     getValues : function(asString){
11803         //if (this.childForms) {
11804             // copy values from the child forms
11805         //    Roo.each(this.childForms, function (f) {
11806         //        this.setValues(f.getValues());
11807         //    }, this);
11808         //}
11809
11810
11811
11812         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11813         if(asString === true){
11814             return fs;
11815         }
11816         return Roo.urlDecode(fs);
11817     },
11818
11819     /**
11820      * Returns the fields in this form as an object with key/value pairs.
11821      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11822      * @return {Object}
11823      */
11824     getFieldValues : function(with_hidden)
11825     {
11826         var items = this.getItems();
11827         var ret = {};
11828         items.each(function(f){
11829             
11830             if (!f.getName()) {
11831                 return;
11832             }
11833             
11834             var v = f.getValue();
11835             
11836             if (f.inputType =='radio') {
11837                 if (typeof(ret[f.getName()]) == 'undefined') {
11838                     ret[f.getName()] = ''; // empty..
11839                 }
11840
11841                 if (!f.el.dom.checked) {
11842                     return;
11843
11844                 }
11845                 v = f.el.dom.value;
11846
11847             }
11848             
11849             if(f.xtype == 'MoneyField'){
11850                 ret[f.currencyName] = f.getCurrency();
11851             }
11852
11853             // not sure if this supported any more..
11854             if ((typeof(v) == 'object') && f.getRawValue) {
11855                 v = f.getRawValue() ; // dates..
11856             }
11857             // combo boxes where name != hiddenName...
11858             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11859                 ret[f.name] = f.getRawValue();
11860             }
11861             ret[f.getName()] = v;
11862         });
11863
11864         return ret;
11865     },
11866
11867     /**
11868      * Clears all invalid messages in this form.
11869      * @return {BasicForm} this
11870      */
11871     clearInvalid : function(){
11872         var items = this.getItems();
11873
11874         items.each(function(f){
11875            f.clearInvalid();
11876         });
11877
11878         return this;
11879     },
11880
11881     /**
11882      * Resets this form.
11883      * @return {BasicForm} this
11884      */
11885     reset : function(){
11886         var items = this.getItems();
11887         items.each(function(f){
11888             f.reset();
11889         });
11890
11891         Roo.each(this.childForms || [], function (f) {
11892             f.reset();
11893         });
11894
11895
11896         return this;
11897     },
11898     
11899     getItems : function()
11900     {
11901         var r=new Roo.util.MixedCollection(false, function(o){
11902             return o.id || (o.id = Roo.id());
11903         });
11904         var iter = function(el) {
11905             if (el.inputEl) {
11906                 r.add(el);
11907             }
11908             if (!el.items) {
11909                 return;
11910             }
11911             Roo.each(el.items,function(e) {
11912                 iter(e);
11913             });
11914         };
11915
11916         iter(this);
11917         return r;
11918     },
11919     
11920     hideFields : function(items)
11921     {
11922         Roo.each(items, function(i){
11923             
11924             var f = this.findField(i);
11925             
11926             if(!f){
11927                 return;
11928             }
11929             
11930             f.hide();
11931             
11932         }, this);
11933     },
11934     
11935     showFields : function(items)
11936     {
11937         Roo.each(items, function(i){
11938             
11939             var f = this.findField(i);
11940             
11941             if(!f){
11942                 return;
11943             }
11944             
11945             f.show();
11946             
11947         }, this);
11948     }
11949
11950 });
11951
11952 Roo.apply(Roo.bootstrap.form.Form, {
11953     
11954     popover : {
11955         
11956         padding : 5,
11957         
11958         isApplied : false,
11959         
11960         isMasked : false,
11961         
11962         form : false,
11963         
11964         target : false,
11965         
11966         toolTip : false,
11967         
11968         intervalID : false,
11969         
11970         maskEl : false,
11971         
11972         apply : function()
11973         {
11974             if(this.isApplied){
11975                 return;
11976             }
11977             
11978             this.maskEl = {
11979                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11980                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11981                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11982                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11983             };
11984             
11985             this.maskEl.top.enableDisplayMode("block");
11986             this.maskEl.left.enableDisplayMode("block");
11987             this.maskEl.bottom.enableDisplayMode("block");
11988             this.maskEl.right.enableDisplayMode("block");
11989             
11990             this.toolTip = new Roo.bootstrap.Tooltip({
11991                 cls : 'roo-form-error-popover',
11992                 alignment : {
11993                     'left' : ['r-l', [-2,0], 'right'],
11994                     'right' : ['l-r', [2,0], 'left'],
11995                     'bottom' : ['tl-bl', [0,2], 'top'],
11996                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11997                 }
11998             });
11999             
12000             this.toolTip.render(Roo.get(document.body));
12001
12002             this.toolTip.el.enableDisplayMode("block");
12003             
12004             Roo.get(document.body).on('click', function(){
12005                 this.unmask();
12006             }, this);
12007             
12008             Roo.get(document.body).on('touchstart', function(){
12009                 this.unmask();
12010             }, this);
12011             
12012             this.isApplied = true
12013         },
12014         
12015         mask : function(form, target)
12016         {
12017             this.form = form;
12018             
12019             this.target = target;
12020             
12021             if(!this.form.errorMask || !target.el){
12022                 return;
12023             }
12024             
12025             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12026             
12027             Roo.log(scrollable);
12028             
12029             var ot = this.target.el.calcOffsetsTo(scrollable);
12030             
12031             var scrollTo = ot[1] - this.form.maskOffset;
12032             
12033             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12034             
12035             scrollable.scrollTo('top', scrollTo);
12036             
12037             var box = this.target.el.getBox();
12038             Roo.log(box);
12039             var zIndex = Roo.bootstrap.Modal.zIndex++;
12040
12041             
12042             this.maskEl.top.setStyle('position', 'absolute');
12043             this.maskEl.top.setStyle('z-index', zIndex);
12044             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12045             this.maskEl.top.setLeft(0);
12046             this.maskEl.top.setTop(0);
12047             this.maskEl.top.show();
12048             
12049             this.maskEl.left.setStyle('position', 'absolute');
12050             this.maskEl.left.setStyle('z-index', zIndex);
12051             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12052             this.maskEl.left.setLeft(0);
12053             this.maskEl.left.setTop(box.y - this.padding);
12054             this.maskEl.left.show();
12055
12056             this.maskEl.bottom.setStyle('position', 'absolute');
12057             this.maskEl.bottom.setStyle('z-index', zIndex);
12058             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12059             this.maskEl.bottom.setLeft(0);
12060             this.maskEl.bottom.setTop(box.bottom + this.padding);
12061             this.maskEl.bottom.show();
12062
12063             this.maskEl.right.setStyle('position', 'absolute');
12064             this.maskEl.right.setStyle('z-index', zIndex);
12065             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12066             this.maskEl.right.setLeft(box.right + this.padding);
12067             this.maskEl.right.setTop(box.y - this.padding);
12068             this.maskEl.right.show();
12069
12070             this.toolTip.bindEl = this.target.el;
12071
12072             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12073
12074             var tip = this.target.blankText;
12075
12076             if(this.target.getValue() !== '' ) {
12077                 
12078                 if (this.target.invalidText.length) {
12079                     tip = this.target.invalidText;
12080                 } else if (this.target.regexText.length){
12081                     tip = this.target.regexText;
12082                 }
12083             }
12084
12085             this.toolTip.show(tip);
12086
12087             this.intervalID = window.setInterval(function() {
12088                 Roo.bootstrap.form.Form.popover.unmask();
12089             }, 10000);
12090
12091             window.onwheel = function(){ return false;};
12092             
12093             (function(){ this.isMasked = true; }).defer(500, this);
12094             
12095         },
12096         
12097         unmask : function()
12098         {
12099             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12100                 return;
12101             }
12102             
12103             this.maskEl.top.setStyle('position', 'absolute');
12104             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12105             this.maskEl.top.hide();
12106
12107             this.maskEl.left.setStyle('position', 'absolute');
12108             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12109             this.maskEl.left.hide();
12110
12111             this.maskEl.bottom.setStyle('position', 'absolute');
12112             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12113             this.maskEl.bottom.hide();
12114
12115             this.maskEl.right.setStyle('position', 'absolute');
12116             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12117             this.maskEl.right.hide();
12118             
12119             this.toolTip.hide();
12120             
12121             this.toolTip.el.hide();
12122             
12123             window.onwheel = function(){ return true;};
12124             
12125             if(this.intervalID){
12126                 window.clearInterval(this.intervalID);
12127                 this.intervalID = false;
12128             }
12129             
12130             this.isMasked = false;
12131             
12132         }
12133         
12134     }
12135     
12136 });
12137
12138 /*
12139  * Based on:
12140  * Ext JS Library 1.1.1
12141  * Copyright(c) 2006-2007, Ext JS, LLC.
12142  *
12143  * Originally Released Under LGPL - original licence link has changed is not relivant.
12144  *
12145  * Fork - LGPL
12146  * <script type="text/javascript">
12147  */
12148 /**
12149  * @class Roo.form.VTypes
12150  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12151  * @static
12152  */
12153 Roo.form.VTypes = function(){
12154     // closure these in so they are only created once.
12155     var alpha = /^[a-zA-Z_]+$/;
12156     var alphanum = /^[a-zA-Z0-9_]+$/;
12157     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12158     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12159
12160     // All these messages and functions are configurable
12161     return {
12162         /**
12163          * The function used to validate email addresses
12164          * @param {String} value The email address
12165          */
12166         'email' : function(v){
12167             return email.test(v);
12168         },
12169         /**
12170          * The error text to display when the email validation function returns false
12171          * @type String
12172          */
12173         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12174         /**
12175          * The keystroke filter mask to be applied on email input
12176          * @type RegExp
12177          */
12178         'emailMask' : /[a-z0-9_\.\-@]/i,
12179
12180         /**
12181          * The function used to validate URLs
12182          * @param {String} value The URL
12183          */
12184         'url' : function(v){
12185             return url.test(v);
12186         },
12187         /**
12188          * The error text to display when the url validation function returns false
12189          * @type String
12190          */
12191         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12192         
12193         /**
12194          * The function used to validate alpha values
12195          * @param {String} value The value
12196          */
12197         'alpha' : function(v){
12198             return alpha.test(v);
12199         },
12200         /**
12201          * The error text to display when the alpha validation function returns false
12202          * @type String
12203          */
12204         'alphaText' : 'This field should only contain letters and _',
12205         /**
12206          * The keystroke filter mask to be applied on alpha input
12207          * @type RegExp
12208          */
12209         'alphaMask' : /[a-z_]/i,
12210
12211         /**
12212          * The function used to validate alphanumeric values
12213          * @param {String} value The value
12214          */
12215         'alphanum' : function(v){
12216             return alphanum.test(v);
12217         },
12218         /**
12219          * The error text to display when the alphanumeric validation function returns false
12220          * @type String
12221          */
12222         'alphanumText' : 'This field should only contain letters, numbers and _',
12223         /**
12224          * The keystroke filter mask to be applied on alphanumeric input
12225          * @type RegExp
12226          */
12227         'alphanumMask' : /[a-z0-9_]/i
12228     };
12229 }();/*
12230  * - LGPL
12231  *
12232  * Input
12233  * 
12234  */
12235
12236 /**
12237  * @class Roo.bootstrap.form.Input
12238  * @extends Roo.bootstrap.Component
12239  * Bootstrap Input class
12240  * @cfg {Boolean} disabled is it disabled
12241  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12242  * @cfg {String} name name of the input
12243  * @cfg {string} fieldLabel - the label associated
12244  * @cfg {string} placeholder - placeholder to put in text.
12245  * @cfg {string} before - input group add on before
12246  * @cfg {string} after - input group add on after
12247  * @cfg {string} size - (lg|sm) or leave empty..
12248  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12249  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12250  * @cfg {Number} md colspan out of 12 for computer-sized screens
12251  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12252  * @cfg {string} value default value of the input
12253  * @cfg {Number} labelWidth set the width of label 
12254  * @cfg {Number} labellg set the width of label (1-12)
12255  * @cfg {Number} labelmd set the width of label (1-12)
12256  * @cfg {Number} labelsm set the width of label (1-12)
12257  * @cfg {Number} labelxs set the width of label (1-12)
12258  * @cfg {String} labelAlign (top|left)
12259  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12260  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12261  * @cfg {String} indicatorpos (left|right) default left
12262  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12263  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12264  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12265  * @cfg {Roo.bootstrap.Button} before Button to show before
12266  * @cfg {Roo.bootstrap.Button} afterButton to show before
12267  * @cfg {String} align (left|center|right) Default left
12268  * @cfg {Boolean} forceFeedback (true|false) Default false
12269  * 
12270  * @constructor
12271  * Create a new Input
12272  * @param {Object} config The config object
12273  */
12274
12275 Roo.bootstrap.form.Input = function(config){
12276     
12277     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12278     
12279     this.addEvents({
12280         /**
12281          * @event focus
12282          * Fires when this field receives input focus.
12283          * @param {Roo.form.Field} this
12284          */
12285         focus : true,
12286         /**
12287          * @event blur
12288          * Fires when this field loses input focus.
12289          * @param {Roo.form.Field} this
12290          */
12291         blur : true,
12292         /**
12293          * @event specialkey
12294          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12295          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12296          * @param {Roo.form.Field} this
12297          * @param {Roo.EventObject} e The event object
12298          */
12299         specialkey : true,
12300         /**
12301          * @event change
12302          * Fires just before the field blurs if the field value has changed.
12303          * @param {Roo.form.Field} this
12304          * @param {Mixed} newValue The new value
12305          * @param {Mixed} oldValue The original value
12306          */
12307         change : true,
12308         /**
12309          * @event invalid
12310          * Fires after the field has been marked as invalid.
12311          * @param {Roo.form.Field} this
12312          * @param {String} msg The validation message
12313          */
12314         invalid : true,
12315         /**
12316          * @event valid
12317          * Fires after the field has been validated with no errors.
12318          * @param {Roo.form.Field} this
12319          */
12320         valid : true,
12321          /**
12322          * @event keyup
12323          * Fires after the key up
12324          * @param {Roo.form.Field} this
12325          * @param {Roo.EventObject}  e The event Object
12326          */
12327         keyup : true,
12328         /**
12329          * @event paste
12330          * Fires after the user pastes into input
12331          * @param {Roo.form.Field} this
12332          * @param {Roo.EventObject}  e The event Object
12333          */
12334         paste : true
12335     });
12336 };
12337
12338 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12339      /**
12340      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12341       automatic validation (defaults to "keyup").
12342      */
12343     validationEvent : "keyup",
12344      /**
12345      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12346      */
12347     validateOnBlur : true,
12348     /**
12349      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12350      */
12351     validationDelay : 250,
12352      /**
12353      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12354      */
12355     focusClass : "x-form-focus",  // not needed???
12356     
12357        
12358     /**
12359      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12360      */
12361     invalidClass : "has-warning",
12362     
12363     /**
12364      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12365      */
12366     validClass : "has-success",
12367     
12368     /**
12369      * @cfg {Boolean} hasFeedback (true|false) default true
12370      */
12371     hasFeedback : true,
12372     
12373     /**
12374      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12375      */
12376     invalidFeedbackClass : "glyphicon-warning-sign",
12377     
12378     /**
12379      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12380      */
12381     validFeedbackClass : "glyphicon-ok",
12382     
12383     /**
12384      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12385      */
12386     selectOnFocus : false,
12387     
12388      /**
12389      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12390      */
12391     maskRe : null,
12392        /**
12393      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12394      */
12395     vtype : null,
12396     
12397       /**
12398      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12399      */
12400     disableKeyFilter : false,
12401     
12402        /**
12403      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12404      */
12405     disabled : false,
12406      /**
12407      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12408      */
12409     allowBlank : true,
12410     /**
12411      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12412      */
12413     blankText : "Please complete this mandatory field",
12414     
12415      /**
12416      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12417      */
12418     minLength : 0,
12419     /**
12420      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12421      */
12422     maxLength : Number.MAX_VALUE,
12423     /**
12424      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12425      */
12426     minLengthText : "The minimum length for this field is {0}",
12427     /**
12428      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12429      */
12430     maxLengthText : "The maximum length for this field is {0}",
12431   
12432     
12433     /**
12434      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12435      * If available, this function will be called only after the basic validators all return true, and will be passed the
12436      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12437      */
12438     validator : null,
12439     /**
12440      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12441      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12442      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12443      */
12444     regex : null,
12445     /**
12446      * @cfg {String} regexText -- Depricated - use Invalid Text
12447      */
12448     regexText : "",
12449     
12450     /**
12451      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12452      */
12453     invalidText : "",
12454     
12455     
12456     
12457     autocomplete: false,
12458     
12459     
12460     fieldLabel : '',
12461     inputType : 'text',
12462     
12463     name : false,
12464     placeholder: false,
12465     before : false,
12466     after : false,
12467     size : false,
12468     hasFocus : false,
12469     preventMark: false,
12470     isFormField : true,
12471     value : '',
12472     labelWidth : 2,
12473     labelAlign : false,
12474     readOnly : false,
12475     align : false,
12476     formatedValue : false,
12477     forceFeedback : false,
12478     
12479     indicatorpos : 'left',
12480     
12481     labellg : 0,
12482     labelmd : 0,
12483     labelsm : 0,
12484     labelxs : 0,
12485     
12486     capture : '',
12487     accept : '',
12488     
12489     parentLabelAlign : function()
12490     {
12491         var parent = this;
12492         while (parent.parent()) {
12493             parent = parent.parent();
12494             if (typeof(parent.labelAlign) !='undefined') {
12495                 return parent.labelAlign;
12496             }
12497         }
12498         return 'left';
12499         
12500     },
12501     
12502     getAutoCreate : function()
12503     {
12504         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12505         
12506         var id = Roo.id();
12507         
12508         var cfg = {};
12509         
12510         if(this.inputType != 'hidden'){
12511             cfg.cls = 'form-group' //input-group
12512         }
12513         
12514         var input =  {
12515             tag: 'input',
12516             id : id,
12517             type : this.inputType,
12518             value : this.value,
12519             cls : 'form-control',
12520             placeholder : this.placeholder || '',
12521             autocomplete : this.autocomplete || 'new-password'
12522         };
12523         if (this.inputType == 'file') {
12524             input.style = 'overflow:hidden'; // why not in CSS?
12525         }
12526         
12527         if(this.capture.length){
12528             input.capture = this.capture;
12529         }
12530         
12531         if(this.accept.length){
12532             input.accept = this.accept + "/*";
12533         }
12534         
12535         if(this.align){
12536             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12537         }
12538         
12539         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12540             input.maxLength = this.maxLength;
12541         }
12542         
12543         if (this.disabled) {
12544             input.disabled=true;
12545         }
12546         
12547         if (this.readOnly) {
12548             input.readonly=true;
12549         }
12550         
12551         if (this.name) {
12552             input.name = this.name;
12553         }
12554         
12555         if (this.size) {
12556             input.cls += ' input-' + this.size;
12557         }
12558         
12559         var settings=this;
12560         ['xs','sm','md','lg'].map(function(size){
12561             if (settings[size]) {
12562                 cfg.cls += ' col-' + size + '-' + settings[size];
12563             }
12564         });
12565         
12566         var inputblock = input;
12567         
12568         var feedback = {
12569             tag: 'span',
12570             cls: 'glyphicon form-control-feedback'
12571         };
12572             
12573         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12574             
12575             inputblock = {
12576                 cls : 'has-feedback',
12577                 cn :  [
12578                     input,
12579                     feedback
12580                 ] 
12581             };  
12582         }
12583         
12584         if (this.before || this.after) {
12585             
12586             inputblock = {
12587                 cls : 'input-group',
12588                 cn :  [] 
12589             };
12590             
12591             if (this.before && typeof(this.before) == 'string') {
12592                 
12593                 inputblock.cn.push({
12594                     tag :'span',
12595                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12596                     html : this.before
12597                 });
12598             }
12599             if (this.before && typeof(this.before) == 'object') {
12600                 this.before = Roo.factory(this.before);
12601                 
12602                 inputblock.cn.push({
12603                     tag :'span',
12604                     cls : 'roo-input-before input-group-prepend   input-group-' +
12605                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12606                 });
12607             }
12608             
12609             inputblock.cn.push(input);
12610             
12611             if (this.after && typeof(this.after) == 'string') {
12612                 inputblock.cn.push({
12613                     tag :'span',
12614                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12615                     html : this.after
12616                 });
12617             }
12618             if (this.after && typeof(this.after) == 'object') {
12619                 this.after = Roo.factory(this.after);
12620                 
12621                 inputblock.cn.push({
12622                     tag :'span',
12623                     cls : 'roo-input-after input-group-append  input-group-' +
12624                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12625                 });
12626             }
12627             
12628             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12629                 inputblock.cls += ' has-feedback';
12630                 inputblock.cn.push(feedback);
12631             }
12632         };
12633         var indicator = {
12634             tag : 'i',
12635             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12636             tooltip : 'This field is required'
12637         };
12638         if (this.allowBlank ) {
12639             indicator.style = this.allowBlank ? ' display:none' : '';
12640         }
12641         if (align ==='left' && this.fieldLabel.length) {
12642             
12643             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12644             
12645             cfg.cn = [
12646                 indicator,
12647                 {
12648                     tag: 'label',
12649                     'for' :  id,
12650                     cls : 'control-label col-form-label',
12651                     html : this.fieldLabel
12652
12653                 },
12654                 {
12655                     cls : "", 
12656                     cn: [
12657                         inputblock
12658                     ]
12659                 }
12660             ];
12661             
12662             var labelCfg = cfg.cn[1];
12663             var contentCfg = cfg.cn[2];
12664             
12665             if(this.indicatorpos == 'right'){
12666                 cfg.cn = [
12667                     {
12668                         tag: 'label',
12669                         'for' :  id,
12670                         cls : 'control-label col-form-label',
12671                         cn : [
12672                             {
12673                                 tag : 'span',
12674                                 html : this.fieldLabel
12675                             },
12676                             indicator
12677                         ]
12678                     },
12679                     {
12680                         cls : "",
12681                         cn: [
12682                             inputblock
12683                         ]
12684                     }
12685
12686                 ];
12687                 
12688                 labelCfg = cfg.cn[0];
12689                 contentCfg = cfg.cn[1];
12690             
12691             }
12692             
12693             if(this.labelWidth > 12){
12694                 labelCfg.style = "width: " + this.labelWidth + 'px';
12695             }
12696             
12697             if(this.labelWidth < 13 && this.labelmd == 0){
12698                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12699             }
12700             
12701             if(this.labellg > 0){
12702                 labelCfg.cls += ' col-lg-' + this.labellg;
12703                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12704             }
12705             
12706             if(this.labelmd > 0){
12707                 labelCfg.cls += ' col-md-' + this.labelmd;
12708                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12709             }
12710             
12711             if(this.labelsm > 0){
12712                 labelCfg.cls += ' col-sm-' + this.labelsm;
12713                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12714             }
12715             
12716             if(this.labelxs > 0){
12717                 labelCfg.cls += ' col-xs-' + this.labelxs;
12718                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12719             }
12720             
12721             
12722         } else if ( this.fieldLabel.length) {
12723                 
12724             
12725             
12726             cfg.cn = [
12727                 {
12728                     tag : 'i',
12729                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12730                     tooltip : 'This field is required',
12731                     style : this.allowBlank ? ' display:none' : '' 
12732                 },
12733                 {
12734                     tag: 'label',
12735                    //cls : 'input-group-addon',
12736                     html : this.fieldLabel
12737
12738                 },
12739
12740                inputblock
12741
12742            ];
12743            
12744            if(this.indicatorpos == 'right'){
12745        
12746                 cfg.cn = [
12747                     {
12748                         tag: 'label',
12749                        //cls : 'input-group-addon',
12750                         html : this.fieldLabel
12751
12752                     },
12753                     {
12754                         tag : 'i',
12755                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12756                         tooltip : 'This field is required',
12757                         style : this.allowBlank ? ' display:none' : '' 
12758                     },
12759
12760                    inputblock
12761
12762                ];
12763
12764             }
12765
12766         } else {
12767             
12768             cfg.cn = [
12769
12770                     inputblock
12771
12772             ];
12773                 
12774                 
12775         };
12776         
12777         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12778            cfg.cls += ' navbar-form';
12779         }
12780         
12781         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12782             // on BS4 we do this only if not form 
12783             cfg.cls += ' navbar-form';
12784             cfg.tag = 'li';
12785         }
12786         
12787         return cfg;
12788         
12789     },
12790     /**
12791      * return the real input element.
12792      */
12793     inputEl: function ()
12794     {
12795         return this.el.select('input.form-control',true).first();
12796     },
12797     
12798     tooltipEl : function()
12799     {
12800         return this.inputEl();
12801     },
12802     
12803     indicatorEl : function()
12804     {
12805         if (Roo.bootstrap.version == 4) {
12806             return false; // not enabled in v4 yet.
12807         }
12808         
12809         var indicator = this.el.select('i.roo-required-indicator',true).first();
12810         
12811         if(!indicator){
12812             return false;
12813         }
12814         
12815         return indicator;
12816         
12817     },
12818     
12819     setDisabled : function(v)
12820     {
12821         var i  = this.inputEl().dom;
12822         if (!v) {
12823             i.removeAttribute('disabled');
12824             return;
12825             
12826         }
12827         i.setAttribute('disabled','true');
12828     },
12829     initEvents : function()
12830     {
12831           
12832         this.inputEl().on("keydown" , this.fireKey,  this);
12833         this.inputEl().on("focus", this.onFocus,  this);
12834         this.inputEl().on("blur", this.onBlur,  this);
12835         
12836         this.inputEl().relayEvent('keyup', this);
12837         this.inputEl().relayEvent('paste', this);
12838         
12839         this.indicator = this.indicatorEl();
12840         
12841         if(this.indicator){
12842             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12843         }
12844  
12845         // reference to original value for reset
12846         this.originalValue = this.getValue();
12847         //Roo.form.TextField.superclass.initEvents.call(this);
12848         if(this.validationEvent == 'keyup'){
12849             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12850             this.inputEl().on('keyup', this.filterValidation, this);
12851         }
12852         else if(this.validationEvent !== false){
12853             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12854         }
12855         
12856         if(this.selectOnFocus){
12857             this.on("focus", this.preFocus, this);
12858             
12859         }
12860         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12861             this.inputEl().on("keypress", this.filterKeys, this);
12862         } else {
12863             this.inputEl().relayEvent('keypress', this);
12864         }
12865        /* if(this.grow){
12866             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12867             this.el.on("click", this.autoSize,  this);
12868         }
12869         */
12870         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12871             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12872         }
12873         
12874         if (typeof(this.before) == 'object') {
12875             this.before.render(this.el.select('.roo-input-before',true).first());
12876         }
12877         if (typeof(this.after) == 'object') {
12878             this.after.render(this.el.select('.roo-input-after',true).first());
12879         }
12880         
12881         this.inputEl().on('change', this.onChange, this);
12882         
12883     },
12884     filterValidation : function(e){
12885         if(!e.isNavKeyPress()){
12886             this.validationTask.delay(this.validationDelay);
12887         }
12888     },
12889      /**
12890      * Validates the field value
12891      * @return {Boolean} True if the value is valid, else false
12892      */
12893     validate : function(){
12894         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12895         if(this.disabled || this.validateValue(this.getRawValue())){
12896             this.markValid();
12897             return true;
12898         }
12899         
12900         this.markInvalid();
12901         return false;
12902     },
12903     
12904     
12905     /**
12906      * Validates a value according to the field's validation rules and marks the field as invalid
12907      * if the validation fails
12908      * @param {Mixed} value The value to validate
12909      * @return {Boolean} True if the value is valid, else false
12910      */
12911     validateValue : function(value)
12912     {
12913         if(this.getVisibilityEl().hasClass('hidden')){
12914             return true;
12915         }
12916         
12917         if(value.length < 1)  { // if it's blank
12918             if(this.allowBlank){
12919                 return true;
12920             }
12921             return false;
12922         }
12923         
12924         if(value.length < this.minLength){
12925             return false;
12926         }
12927         if(value.length > this.maxLength){
12928             return false;
12929         }
12930         if(this.vtype){
12931             var vt = Roo.form.VTypes;
12932             if(!vt[this.vtype](value, this)){
12933                 return false;
12934             }
12935         }
12936         if(typeof this.validator == "function"){
12937             var msg = this.validator(value);
12938             if(msg !== true){
12939                 return false;
12940             }
12941             if (typeof(msg) == 'string') {
12942                 this.invalidText = msg;
12943             }
12944         }
12945         
12946         if(this.regex && !this.regex.test(value)){
12947             return false;
12948         }
12949         
12950         return true;
12951     },
12952     
12953      // private
12954     fireKey : function(e){
12955         //Roo.log('field ' + e.getKey());
12956         if(e.isNavKeyPress()){
12957             this.fireEvent("specialkey", this, e);
12958         }
12959     },
12960     focus : function (selectText){
12961         if(this.rendered){
12962             this.inputEl().focus();
12963             if(selectText === true){
12964                 this.inputEl().dom.select();
12965             }
12966         }
12967         return this;
12968     } ,
12969     
12970     onFocus : function(){
12971         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12972            // this.el.addClass(this.focusClass);
12973         }
12974         if(!this.hasFocus){
12975             this.hasFocus = true;
12976             this.startValue = this.getValue();
12977             this.fireEvent("focus", this);
12978         }
12979     },
12980     
12981     beforeBlur : Roo.emptyFn,
12982
12983     
12984     // private
12985     onBlur : function(){
12986         this.beforeBlur();
12987         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12988             //this.el.removeClass(this.focusClass);
12989         }
12990         this.hasFocus = false;
12991         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12992             this.validate();
12993         }
12994         var v = this.getValue();
12995         if(String(v) !== String(this.startValue)){
12996             this.fireEvent('change', this, v, this.startValue);
12997         }
12998         this.fireEvent("blur", this);
12999     },
13000     
13001     onChange : function(e)
13002     {
13003         var v = this.getValue();
13004         if(String(v) !== String(this.startValue)){
13005             this.fireEvent('change', this, v, this.startValue);
13006         }
13007         
13008     },
13009     
13010     /**
13011      * Resets the current field value to the originally loaded value and clears any validation messages
13012      */
13013     reset : function(){
13014         this.setValue(this.originalValue);
13015         this.validate();
13016     },
13017      /**
13018      * Returns the name of the field
13019      * @return {Mixed} name The name field
13020      */
13021     getName: function(){
13022         return this.name;
13023     },
13024      /**
13025      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13026      * @return {Mixed} value The field value
13027      */
13028     getValue : function(){
13029         
13030         var v = this.inputEl().getValue();
13031         
13032         return v;
13033     },
13034     /**
13035      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13036      * @return {Mixed} value The field value
13037      */
13038     getRawValue : function(){
13039         var v = this.inputEl().getValue();
13040         
13041         return v;
13042     },
13043     
13044     /**
13045      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13046      * @param {Mixed} value The value to set
13047      */
13048     setRawValue : function(v){
13049         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13050     },
13051     
13052     selectText : function(start, end){
13053         var v = this.getRawValue();
13054         if(v.length > 0){
13055             start = start === undefined ? 0 : start;
13056             end = end === undefined ? v.length : end;
13057             var d = this.inputEl().dom;
13058             if(d.setSelectionRange){
13059                 d.setSelectionRange(start, end);
13060             }else if(d.createTextRange){
13061                 var range = d.createTextRange();
13062                 range.moveStart("character", start);
13063                 range.moveEnd("character", v.length-end);
13064                 range.select();
13065             }
13066         }
13067     },
13068     
13069     /**
13070      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13071      * @param {Mixed} value The value to set
13072      */
13073     setValue : function(v){
13074         this.value = v;
13075         if(this.rendered){
13076             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13077             this.validate();
13078         }
13079     },
13080     
13081     /*
13082     processValue : function(value){
13083         if(this.stripCharsRe){
13084             var newValue = value.replace(this.stripCharsRe, '');
13085             if(newValue !== value){
13086                 this.setRawValue(newValue);
13087                 return newValue;
13088             }
13089         }
13090         return value;
13091     },
13092   */
13093     preFocus : function(){
13094         
13095         if(this.selectOnFocus){
13096             this.inputEl().dom.select();
13097         }
13098     },
13099     filterKeys : function(e){
13100         var k = e.getKey();
13101         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13102             return;
13103         }
13104         var c = e.getCharCode(), cc = String.fromCharCode(c);
13105         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13106             return;
13107         }
13108         if(!this.maskRe.test(cc)){
13109             e.stopEvent();
13110         }
13111     },
13112      /**
13113      * Clear any invalid styles/messages for this field
13114      */
13115     clearInvalid : function(){
13116         
13117         if(!this.el || this.preventMark){ // not rendered
13118             return;
13119         }
13120         
13121         
13122         this.el.removeClass([this.invalidClass, 'is-invalid']);
13123         
13124         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13125             
13126             var feedback = this.el.select('.form-control-feedback', true).first();
13127             
13128             if(feedback){
13129                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13130             }
13131             
13132         }
13133         
13134         if(this.indicator){
13135             this.indicator.removeClass('visible');
13136             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13137         }
13138         
13139         this.fireEvent('valid', this);
13140     },
13141     
13142      /**
13143      * Mark this field as valid
13144      */
13145     markValid : function()
13146     {
13147         if(!this.el  || this.preventMark){ // not rendered...
13148             return;
13149         }
13150         
13151         this.el.removeClass([this.invalidClass, this.validClass]);
13152         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13153
13154         var feedback = this.el.select('.form-control-feedback', true).first();
13155             
13156         if(feedback){
13157             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13158         }
13159         
13160         if(this.indicator){
13161             this.indicator.removeClass('visible');
13162             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13163         }
13164         
13165         if(this.disabled){
13166             return;
13167         }
13168         
13169            
13170         if(this.allowBlank && !this.getRawValue().length){
13171             return;
13172         }
13173         if (Roo.bootstrap.version == 3) {
13174             this.el.addClass(this.validClass);
13175         } else {
13176             this.inputEl().addClass('is-valid');
13177         }
13178
13179         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13180             
13181             var feedback = this.el.select('.form-control-feedback', true).first();
13182             
13183             if(feedback){
13184                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13185                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13186             }
13187             
13188         }
13189         
13190         this.fireEvent('valid', this);
13191     },
13192     
13193      /**
13194      * Mark this field as invalid
13195      * @param {String} msg The validation message
13196      */
13197     markInvalid : function(msg)
13198     {
13199         if(!this.el  || this.preventMark){ // not rendered
13200             return;
13201         }
13202         
13203         this.el.removeClass([this.invalidClass, this.validClass]);
13204         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13205         
13206         var feedback = this.el.select('.form-control-feedback', true).first();
13207             
13208         if(feedback){
13209             this.el.select('.form-control-feedback', true).first().removeClass(
13210                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13211         }
13212
13213         if(this.disabled){
13214             return;
13215         }
13216         
13217         if(this.allowBlank && !this.getRawValue().length){
13218             return;
13219         }
13220         
13221         if(this.indicator){
13222             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13223             this.indicator.addClass('visible');
13224         }
13225         if (Roo.bootstrap.version == 3) {
13226             this.el.addClass(this.invalidClass);
13227         } else {
13228             this.inputEl().addClass('is-invalid');
13229         }
13230         
13231         
13232         
13233         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13234             
13235             var feedback = this.el.select('.form-control-feedback', true).first();
13236             
13237             if(feedback){
13238                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13239                 
13240                 if(this.getValue().length || this.forceFeedback){
13241                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13242                 }
13243                 
13244             }
13245             
13246         }
13247         
13248         this.fireEvent('invalid', this, msg);
13249     },
13250     // private
13251     SafariOnKeyDown : function(event)
13252     {
13253         // this is a workaround for a password hang bug on chrome/ webkit.
13254         if (this.inputEl().dom.type != 'password') {
13255             return;
13256         }
13257         
13258         var isSelectAll = false;
13259         
13260         if(this.inputEl().dom.selectionEnd > 0){
13261             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13262         }
13263         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13264             event.preventDefault();
13265             this.setValue('');
13266             return;
13267         }
13268         
13269         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13270             
13271             event.preventDefault();
13272             // this is very hacky as keydown always get's upper case.
13273             //
13274             var cc = String.fromCharCode(event.getCharCode());
13275             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13276             
13277         }
13278     },
13279     adjustWidth : function(tag, w){
13280         tag = tag.toLowerCase();
13281         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13282             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13283                 if(tag == 'input'){
13284                     return w + 2;
13285                 }
13286                 if(tag == 'textarea'){
13287                     return w-2;
13288                 }
13289             }else if(Roo.isOpera){
13290                 if(tag == 'input'){
13291                     return w + 2;
13292                 }
13293                 if(tag == 'textarea'){
13294                     return w-2;
13295                 }
13296             }
13297         }
13298         return w;
13299     },
13300     
13301     setFieldLabel : function(v)
13302     {
13303         if(!this.rendered){
13304             return;
13305         }
13306         
13307         if(this.indicatorEl()){
13308             var ar = this.el.select('label > span',true);
13309             
13310             if (ar.elements.length) {
13311                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13312                 this.fieldLabel = v;
13313                 return;
13314             }
13315             
13316             var br = this.el.select('label',true);
13317             
13318             if(br.elements.length) {
13319                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13320                 this.fieldLabel = v;
13321                 return;
13322             }
13323             
13324             Roo.log('Cannot Found any of label > span || label in input');
13325             return;
13326         }
13327         
13328         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13329         this.fieldLabel = v;
13330         
13331         
13332     }
13333 });
13334
13335  
13336 /*
13337  * - LGPL
13338  *
13339  * Input
13340  * 
13341  */
13342
13343 /**
13344  * @class Roo.bootstrap.form.TextArea
13345  * @extends Roo.bootstrap.form.Input
13346  * Bootstrap TextArea class
13347  * @cfg {Number} cols Specifies the visible width of a text area
13348  * @cfg {Number} rows Specifies the visible number of lines in a text area
13349  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13350  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13351  * @cfg {string} html text
13352  * 
13353  * @constructor
13354  * Create a new TextArea
13355  * @param {Object} config The config object
13356  */
13357
13358 Roo.bootstrap.form.TextArea = function(config){
13359     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13360    
13361 };
13362
13363 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13364      
13365     cols : false,
13366     rows : 5,
13367     readOnly : false,
13368     warp : 'soft',
13369     resize : false,
13370     value: false,
13371     html: false,
13372     
13373     getAutoCreate : function(){
13374         
13375         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13376         
13377         var id = Roo.id();
13378         
13379         var cfg = {};
13380         
13381         if(this.inputType != 'hidden'){
13382             cfg.cls = 'form-group' //input-group
13383         }
13384         
13385         var input =  {
13386             tag: 'textarea',
13387             id : id,
13388             warp : this.warp,
13389             rows : this.rows,
13390             value : this.value || '',
13391             html: this.html || '',
13392             cls : 'form-control',
13393             placeholder : this.placeholder || '' 
13394             
13395         };
13396         
13397         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13398             input.maxLength = this.maxLength;
13399         }
13400         
13401         if(this.resize){
13402             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13403         }
13404         
13405         if(this.cols){
13406             input.cols = this.cols;
13407         }
13408         
13409         if (this.readOnly) {
13410             input.readonly = true;
13411         }
13412         
13413         if (this.name) {
13414             input.name = this.name;
13415         }
13416         
13417         if (this.size) {
13418             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13419         }
13420         
13421         var settings=this;
13422         ['xs','sm','md','lg'].map(function(size){
13423             if (settings[size]) {
13424                 cfg.cls += ' col-' + size + '-' + settings[size];
13425             }
13426         });
13427         
13428         var inputblock = input;
13429         
13430         if(this.hasFeedback && !this.allowBlank){
13431             
13432             var feedback = {
13433                 tag: 'span',
13434                 cls: 'glyphicon form-control-feedback'
13435             };
13436
13437             inputblock = {
13438                 cls : 'has-feedback',
13439                 cn :  [
13440                     input,
13441                     feedback
13442                 ] 
13443             };  
13444         }
13445         
13446         
13447         if (this.before || this.after) {
13448             
13449             inputblock = {
13450                 cls : 'input-group',
13451                 cn :  [] 
13452             };
13453             if (this.before) {
13454                 inputblock.cn.push({
13455                     tag :'span',
13456                     cls : 'input-group-addon',
13457                     html : this.before
13458                 });
13459             }
13460             
13461             inputblock.cn.push(input);
13462             
13463             if(this.hasFeedback && !this.allowBlank){
13464                 inputblock.cls += ' has-feedback';
13465                 inputblock.cn.push(feedback);
13466             }
13467             
13468             if (this.after) {
13469                 inputblock.cn.push({
13470                     tag :'span',
13471                     cls : 'input-group-addon',
13472                     html : this.after
13473                 });
13474             }
13475             
13476         }
13477         
13478         if (align ==='left' && this.fieldLabel.length) {
13479             cfg.cn = [
13480                 {
13481                     tag: 'label',
13482                     'for' :  id,
13483                     cls : 'control-label',
13484                     html : this.fieldLabel
13485                 },
13486                 {
13487                     cls : "",
13488                     cn: [
13489                         inputblock
13490                     ]
13491                 }
13492
13493             ];
13494             
13495             if(this.labelWidth > 12){
13496                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13497             }
13498
13499             if(this.labelWidth < 13 && this.labelmd == 0){
13500                 this.labelmd = this.labelWidth;
13501             }
13502
13503             if(this.labellg > 0){
13504                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13505                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13506             }
13507
13508             if(this.labelmd > 0){
13509                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13510                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13511             }
13512
13513             if(this.labelsm > 0){
13514                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13515                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13516             }
13517
13518             if(this.labelxs > 0){
13519                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13520                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13521             }
13522             
13523         } else if ( this.fieldLabel.length) {
13524             cfg.cn = [
13525
13526                {
13527                    tag: 'label',
13528                    //cls : 'input-group-addon',
13529                    html : this.fieldLabel
13530
13531                },
13532
13533                inputblock
13534
13535            ];
13536
13537         } else {
13538
13539             cfg.cn = [
13540
13541                 inputblock
13542
13543             ];
13544                 
13545         }
13546         
13547         if (this.disabled) {
13548             input.disabled=true;
13549         }
13550         
13551         return cfg;
13552         
13553     },
13554     /**
13555      * return the real textarea element.
13556      */
13557     inputEl: function ()
13558     {
13559         return this.el.select('textarea.form-control',true).first();
13560     },
13561     
13562     /**
13563      * Clear any invalid styles/messages for this field
13564      */
13565     clearInvalid : function()
13566     {
13567         
13568         if(!this.el || this.preventMark){ // not rendered
13569             return;
13570         }
13571         
13572         var label = this.el.select('label', true).first();
13573         var icon = this.el.select('i.fa-star', true).first();
13574         
13575         if(label && icon){
13576             icon.remove();
13577         }
13578         this.el.removeClass( this.validClass);
13579         this.inputEl().removeClass('is-invalid');
13580          
13581         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13582             
13583             var feedback = this.el.select('.form-control-feedback', true).first();
13584             
13585             if(feedback){
13586                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13587             }
13588             
13589         }
13590         
13591         this.fireEvent('valid', this);
13592     },
13593     
13594      /**
13595      * Mark this field as valid
13596      */
13597     markValid : function()
13598     {
13599         if(!this.el  || this.preventMark){ // not rendered
13600             return;
13601         }
13602         
13603         this.el.removeClass([this.invalidClass, this.validClass]);
13604         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13605         
13606         var feedback = this.el.select('.form-control-feedback', true).first();
13607             
13608         if(feedback){
13609             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13610         }
13611
13612         if(this.disabled || this.allowBlank){
13613             return;
13614         }
13615         
13616         var label = this.el.select('label', true).first();
13617         var icon = this.el.select('i.fa-star', true).first();
13618         
13619         if(label && icon){
13620             icon.remove();
13621         }
13622         if (Roo.bootstrap.version == 3) {
13623             this.el.addClass(this.validClass);
13624         } else {
13625             this.inputEl().addClass('is-valid');
13626         }
13627         
13628         
13629         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13630             
13631             var feedback = this.el.select('.form-control-feedback', true).first();
13632             
13633             if(feedback){
13634                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13635                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13636             }
13637             
13638         }
13639         
13640         this.fireEvent('valid', this);
13641     },
13642     
13643      /**
13644      * Mark this field as invalid
13645      * @param {String} msg The validation message
13646      */
13647     markInvalid : function(msg)
13648     {
13649         if(!this.el  || this.preventMark){ // not rendered
13650             return;
13651         }
13652         
13653         this.el.removeClass([this.invalidClass, this.validClass]);
13654         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13655         
13656         var feedback = this.el.select('.form-control-feedback', true).first();
13657             
13658         if(feedback){
13659             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13660         }
13661
13662         if(this.disabled || this.allowBlank){
13663             return;
13664         }
13665         
13666         var label = this.el.select('label', true).first();
13667         var icon = this.el.select('i.fa-star', true).first();
13668         
13669         if(!this.getValue().length && label && !icon){
13670             this.el.createChild({
13671                 tag : 'i',
13672                 cls : 'text-danger fa fa-lg fa-star',
13673                 tooltip : 'This field is required',
13674                 style : 'margin-right:5px;'
13675             }, label, true);
13676         }
13677         
13678         if (Roo.bootstrap.version == 3) {
13679             this.el.addClass(this.invalidClass);
13680         } else {
13681             this.inputEl().addClass('is-invalid');
13682         }
13683         
13684         // fixme ... this may be depricated need to test..
13685         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13686             
13687             var feedback = this.el.select('.form-control-feedback', true).first();
13688             
13689             if(feedback){
13690                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13691                 
13692                 if(this.getValue().length || this.forceFeedback){
13693                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13694                 }
13695                 
13696             }
13697             
13698         }
13699         
13700         this.fireEvent('invalid', this, msg);
13701     }
13702 });
13703
13704  
13705 /*
13706  * - LGPL
13707  *
13708  * trigger field - base class for combo..
13709  * 
13710  */
13711  
13712 /**
13713  * @class Roo.bootstrap.form.TriggerField
13714  * @extends Roo.bootstrap.form.Input
13715  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13716  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13717  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13718  * for which you can provide a custom implementation.  For example:
13719  * <pre><code>
13720 var trigger = new Roo.bootstrap.form.TriggerField();
13721 trigger.onTriggerClick = myTriggerFn;
13722 trigger.applyTo('my-field');
13723 </code></pre>
13724  *
13725  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13726  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13727  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13728  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13729  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13730
13731  * @constructor
13732  * Create a new TriggerField.
13733  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13734  * to the base TextField)
13735  */
13736 Roo.bootstrap.form.TriggerField = function(config){
13737     this.mimicing = false;
13738     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13739 };
13740
13741 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13742     /**
13743      * @cfg {String} triggerClass A CSS class to apply to the trigger
13744      */
13745      /**
13746      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13747      */
13748     hideTrigger:false,
13749
13750     /**
13751      * @cfg {Boolean} removable (true|false) special filter default false
13752      */
13753     removable : false,
13754     
13755     /** @cfg {Boolean} grow @hide */
13756     /** @cfg {Number} growMin @hide */
13757     /** @cfg {Number} growMax @hide */
13758
13759     /**
13760      * @hide 
13761      * @method
13762      */
13763     autoSize: Roo.emptyFn,
13764     // private
13765     monitorTab : true,
13766     // private
13767     deferHeight : true,
13768
13769     
13770     actionMode : 'wrap',
13771     
13772     caret : false,
13773     
13774     
13775     getAutoCreate : function(){
13776        
13777         var align = this.labelAlign || this.parentLabelAlign();
13778         
13779         var id = Roo.id();
13780         
13781         var cfg = {
13782             cls: 'form-group' //input-group
13783         };
13784         
13785         
13786         var input =  {
13787             tag: 'input',
13788             id : id,
13789             type : this.inputType,
13790             cls : 'form-control',
13791             autocomplete: 'new-password',
13792             placeholder : this.placeholder || '' 
13793             
13794         };
13795         if (this.name) {
13796             input.name = this.name;
13797         }
13798         if (this.size) {
13799             input.cls += ' input-' + this.size;
13800         }
13801         
13802         if (this.disabled) {
13803             input.disabled=true;
13804         }
13805         
13806         var inputblock = input;
13807         
13808         if(this.hasFeedback && !this.allowBlank){
13809             
13810             var feedback = {
13811                 tag: 'span',
13812                 cls: 'glyphicon form-control-feedback'
13813             };
13814             
13815             if(this.removable && !this.editable  ){
13816                 inputblock = {
13817                     cls : 'has-feedback',
13818                     cn :  [
13819                         inputblock,
13820                         {
13821                             tag: 'button',
13822                             html : 'x',
13823                             cls : 'roo-combo-removable-btn close'
13824                         },
13825                         feedback
13826                     ] 
13827                 };
13828             } else {
13829                 inputblock = {
13830                     cls : 'has-feedback',
13831                     cn :  [
13832                         inputblock,
13833                         feedback
13834                     ] 
13835                 };
13836             }
13837
13838         } else {
13839             if(this.removable && !this.editable ){
13840                 inputblock = {
13841                     cls : 'roo-removable',
13842                     cn :  [
13843                         inputblock,
13844                         {
13845                             tag: 'button',
13846                             html : 'x',
13847                             cls : 'roo-combo-removable-btn close'
13848                         }
13849                     ] 
13850                 };
13851             }
13852         }
13853         
13854         if (this.before || this.after) {
13855             
13856             inputblock = {
13857                 cls : 'input-group',
13858                 cn :  [] 
13859             };
13860             if (this.before) {
13861                 inputblock.cn.push({
13862                     tag :'span',
13863                     cls : 'input-group-addon input-group-prepend input-group-text',
13864                     html : this.before
13865                 });
13866             }
13867             
13868             inputblock.cn.push(input);
13869             
13870             if(this.hasFeedback && !this.allowBlank){
13871                 inputblock.cls += ' has-feedback';
13872                 inputblock.cn.push(feedback);
13873             }
13874             
13875             if (this.after) {
13876                 inputblock.cn.push({
13877                     tag :'span',
13878                     cls : 'input-group-addon input-group-append input-group-text',
13879                     html : this.after
13880                 });
13881             }
13882             
13883         };
13884         
13885       
13886         
13887         var ibwrap = inputblock;
13888         
13889         if(this.multiple){
13890             ibwrap = {
13891                 tag: 'ul',
13892                 cls: 'roo-select2-choices',
13893                 cn:[
13894                     {
13895                         tag: 'li',
13896                         cls: 'roo-select2-search-field',
13897                         cn: [
13898
13899                             inputblock
13900                         ]
13901                     }
13902                 ]
13903             };
13904                 
13905         }
13906         
13907         var combobox = {
13908             cls: 'roo-select2-container input-group',
13909             cn: [
13910                  {
13911                     tag: 'input',
13912                     type : 'hidden',
13913                     cls: 'form-hidden-field'
13914                 },
13915                 ibwrap
13916             ]
13917         };
13918         
13919         if(!this.multiple && this.showToggleBtn){
13920             
13921             var caret = {
13922                         tag: 'span',
13923                         cls: 'caret'
13924              };
13925             if (this.caret != false) {
13926                 caret = {
13927                      tag: 'i',
13928                      cls: 'fa fa-' + this.caret
13929                 };
13930                 
13931             }
13932             
13933             combobox.cn.push({
13934                 tag :'span',
13935                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13936                 cn : [
13937                     Roo.bootstrap.version == 3 ? caret : '',
13938                     {
13939                         tag: 'span',
13940                         cls: 'combobox-clear',
13941                         cn  : [
13942                             {
13943                                 tag : 'i',
13944                                 cls: 'icon-remove'
13945                             }
13946                         ]
13947                     }
13948                 ]
13949
13950             })
13951         }
13952         
13953         if(this.multiple){
13954             combobox.cls += ' roo-select2-container-multi';
13955         }
13956          var indicator = {
13957             tag : 'i',
13958             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13959             tooltip : 'This field is required'
13960         };
13961         if (Roo.bootstrap.version == 4) {
13962             indicator = {
13963                 tag : 'i',
13964                 style : 'display:none'
13965             };
13966         }
13967         
13968         
13969         if (align ==='left' && this.fieldLabel.length) {
13970             
13971             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13972
13973             cfg.cn = [
13974                 indicator,
13975                 {
13976                     tag: 'label',
13977                     'for' :  id,
13978                     cls : 'control-label',
13979                     html : this.fieldLabel
13980
13981                 },
13982                 {
13983                     cls : "", 
13984                     cn: [
13985                         combobox
13986                     ]
13987                 }
13988
13989             ];
13990             
13991             var labelCfg = cfg.cn[1];
13992             var contentCfg = cfg.cn[2];
13993             
13994             if(this.indicatorpos == 'right'){
13995                 cfg.cn = [
13996                     {
13997                         tag: 'label',
13998                         'for' :  id,
13999                         cls : 'control-label',
14000                         cn : [
14001                             {
14002                                 tag : 'span',
14003                                 html : this.fieldLabel
14004                             },
14005                             indicator
14006                         ]
14007                     },
14008                     {
14009                         cls : "", 
14010                         cn: [
14011                             combobox
14012                         ]
14013                     }
14014
14015                 ];
14016                 
14017                 labelCfg = cfg.cn[0];
14018                 contentCfg = cfg.cn[1];
14019             }
14020             
14021             if(this.labelWidth > 12){
14022                 labelCfg.style = "width: " + this.labelWidth + 'px';
14023             }
14024             
14025             if(this.labelWidth < 13 && this.labelmd == 0){
14026                 this.labelmd = this.labelWidth;
14027             }
14028             
14029             if(this.labellg > 0){
14030                 labelCfg.cls += ' col-lg-' + this.labellg;
14031                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14032             }
14033             
14034             if(this.labelmd > 0){
14035                 labelCfg.cls += ' col-md-' + this.labelmd;
14036                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14037             }
14038             
14039             if(this.labelsm > 0){
14040                 labelCfg.cls += ' col-sm-' + this.labelsm;
14041                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14042             }
14043             
14044             if(this.labelxs > 0){
14045                 labelCfg.cls += ' col-xs-' + this.labelxs;
14046                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14047             }
14048             
14049         } else if ( this.fieldLabel.length) {
14050 //                Roo.log(" label");
14051             cfg.cn = [
14052                 indicator,
14053                {
14054                    tag: 'label',
14055                    //cls : 'input-group-addon',
14056                    html : this.fieldLabel
14057
14058                },
14059
14060                combobox
14061
14062             ];
14063             
14064             if(this.indicatorpos == 'right'){
14065                 
14066                 cfg.cn = [
14067                     {
14068                        tag: 'label',
14069                        cn : [
14070                            {
14071                                tag : 'span',
14072                                html : this.fieldLabel
14073                            },
14074                            indicator
14075                        ]
14076
14077                     },
14078                     combobox
14079
14080                 ];
14081
14082             }
14083
14084         } else {
14085             
14086 //                Roo.log(" no label && no align");
14087                 cfg = combobox
14088                      
14089                 
14090         }
14091         
14092         var settings=this;
14093         ['xs','sm','md','lg'].map(function(size){
14094             if (settings[size]) {
14095                 cfg.cls += ' col-' + size + '-' + settings[size];
14096             }
14097         });
14098         
14099         return cfg;
14100         
14101     },
14102     
14103     
14104     
14105     // private
14106     onResize : function(w, h){
14107 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14108 //        if(typeof w == 'number'){
14109 //            var x = w - this.trigger.getWidth();
14110 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14111 //            this.trigger.setStyle('left', x+'px');
14112 //        }
14113     },
14114
14115     // private
14116     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14117
14118     // private
14119     getResizeEl : function(){
14120         return this.inputEl();
14121     },
14122
14123     // private
14124     getPositionEl : function(){
14125         return this.inputEl();
14126     },
14127
14128     // private
14129     alignErrorIcon : function(){
14130         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14131     },
14132
14133     // private
14134     initEvents : function(){
14135         
14136         this.createList();
14137         
14138         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14139         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14140         if(!this.multiple && this.showToggleBtn){
14141             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14142             if(this.hideTrigger){
14143                 this.trigger.setDisplayed(false);
14144             }
14145             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14146         }
14147         
14148         if(this.multiple){
14149             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14150         }
14151         
14152         if(this.removable && !this.editable && !this.tickable){
14153             var close = this.closeTriggerEl();
14154             
14155             if(close){
14156                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14157                 close.on('click', this.removeBtnClick, this, close);
14158             }
14159         }
14160         
14161         //this.trigger.addClassOnOver('x-form-trigger-over');
14162         //this.trigger.addClassOnClick('x-form-trigger-click');
14163         
14164         //if(!this.width){
14165         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14166         //}
14167     },
14168     
14169     closeTriggerEl : function()
14170     {
14171         var close = this.el.select('.roo-combo-removable-btn', true).first();
14172         return close ? close : false;
14173     },
14174     
14175     removeBtnClick : function(e, h, el)
14176     {
14177         e.preventDefault();
14178         
14179         if(this.fireEvent("remove", this) !== false){
14180             this.reset();
14181             this.fireEvent("afterremove", this)
14182         }
14183     },
14184     
14185     createList : function()
14186     {
14187         this.list = Roo.get(document.body).createChild({
14188             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14189             cls: 'typeahead typeahead-long dropdown-menu shadow',
14190             style: 'display:none'
14191         });
14192         
14193         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14194         
14195     },
14196
14197     // private
14198     initTrigger : function(){
14199        
14200     },
14201
14202     // private
14203     onDestroy : function(){
14204         if(this.trigger){
14205             this.trigger.removeAllListeners();
14206           //  this.trigger.remove();
14207         }
14208         //if(this.wrap){
14209         //    this.wrap.remove();
14210         //}
14211         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14212     },
14213
14214     // private
14215     onFocus : function(){
14216         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14217         /*
14218         if(!this.mimicing){
14219             this.wrap.addClass('x-trigger-wrap-focus');
14220             this.mimicing = true;
14221             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14222             if(this.monitorTab){
14223                 this.el.on("keydown", this.checkTab, this);
14224             }
14225         }
14226         */
14227     },
14228
14229     // private
14230     checkTab : function(e){
14231         if(e.getKey() == e.TAB){
14232             this.triggerBlur();
14233         }
14234     },
14235
14236     // private
14237     onBlur : function(){
14238         // do nothing
14239     },
14240
14241     // private
14242     mimicBlur : function(e, t){
14243         /*
14244         if(!this.wrap.contains(t) && this.validateBlur()){
14245             this.triggerBlur();
14246         }
14247         */
14248     },
14249
14250     // private
14251     triggerBlur : function(){
14252         this.mimicing = false;
14253         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14254         if(this.monitorTab){
14255             this.el.un("keydown", this.checkTab, this);
14256         }
14257         //this.wrap.removeClass('x-trigger-wrap-focus');
14258         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14259     },
14260
14261     // private
14262     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14263     validateBlur : function(e, t){
14264         return true;
14265     },
14266
14267     // private
14268     onDisable : function(){
14269         this.inputEl().dom.disabled = true;
14270         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14271         //if(this.wrap){
14272         //    this.wrap.addClass('x-item-disabled');
14273         //}
14274     },
14275
14276     // private
14277     onEnable : function(){
14278         this.inputEl().dom.disabled = false;
14279         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14280         //if(this.wrap){
14281         //    this.el.removeClass('x-item-disabled');
14282         //}
14283     },
14284
14285     // private
14286     onShow : function(){
14287         var ae = this.getActionEl();
14288         
14289         if(ae){
14290             ae.dom.style.display = '';
14291             ae.dom.style.visibility = 'visible';
14292         }
14293     },
14294
14295     // private
14296     
14297     onHide : function(){
14298         var ae = this.getActionEl();
14299         ae.dom.style.display = 'none';
14300     },
14301
14302     /**
14303      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14304      * by an implementing function.
14305      * @method
14306      * @param {EventObject} e
14307      */
14308     onTriggerClick : Roo.emptyFn
14309 });
14310  
14311 /*
14312 * Licence: LGPL
14313 */
14314
14315 /**
14316  * @class Roo.bootstrap.form.CardUploader
14317  * @extends Roo.bootstrap.Button
14318  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14319  * @cfg {Number} errorTimeout default 3000
14320  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14321  * @cfg {Array}  html The button text.
14322
14323  *
14324  * @constructor
14325  * Create a new CardUploader
14326  * @param {Object} config The config object
14327  */
14328
14329 Roo.bootstrap.form.CardUploader = function(config){
14330     
14331  
14332     
14333     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14334     
14335     
14336     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14337         return r.data.id
14338      });
14339     
14340      this.addEvents({
14341          // raw events
14342         /**
14343          * @event preview
14344          * When a image is clicked on - and needs to display a slideshow or similar..
14345          * @param {Roo.bootstrap.Card} this
14346          * @param {Object} The image information data 
14347          *
14348          */
14349         'preview' : true,
14350          /**
14351          * @event download
14352          * When a the download link is clicked
14353          * @param {Roo.bootstrap.Card} this
14354          * @param {Object} The image information data  contains 
14355          */
14356         'download' : true
14357         
14358     });
14359 };
14360  
14361 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14362     
14363      
14364     errorTimeout : 3000,
14365      
14366     images : false,
14367    
14368     fileCollection : false,
14369     allowBlank : true,
14370     
14371     getAutoCreate : function()
14372     {
14373         
14374         var cfg =  {
14375             cls :'form-group' ,
14376             cn : [
14377                
14378                 {
14379                     tag: 'label',
14380                    //cls : 'input-group-addon',
14381                     html : this.fieldLabel
14382
14383                 },
14384
14385                 {
14386                     tag: 'input',
14387                     type : 'hidden',
14388                     name : this.name,
14389                     value : this.value,
14390                     cls : 'd-none  form-control'
14391                 },
14392                 
14393                 {
14394                     tag: 'input',
14395                     multiple : 'multiple',
14396                     type : 'file',
14397                     cls : 'd-none  roo-card-upload-selector'
14398                 },
14399                 
14400                 {
14401                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14402                 },
14403                 {
14404                     cls : 'card-columns roo-card-uploader-container'
14405                 }
14406
14407             ]
14408         };
14409            
14410          
14411         return cfg;
14412     },
14413     
14414     getChildContainer : function() /// what children are added to.
14415     {
14416         return this.containerEl;
14417     },
14418    
14419     getButtonContainer : function() /// what children are added to.
14420     {
14421         return this.el.select(".roo-card-uploader-button-container").first();
14422     },
14423    
14424     initEvents : function()
14425     {
14426         
14427         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14428         
14429         var t = this;
14430         this.addxtype({
14431             xns: Roo.bootstrap,
14432
14433             xtype : 'Button',
14434             container_method : 'getButtonContainer' ,            
14435             html :  this.html, // fix changable?
14436             cls : 'w-100 ',
14437             listeners : {
14438                 'click' : function(btn, e) {
14439                     t.onClick(e);
14440                 }
14441             }
14442         });
14443         
14444         
14445         
14446         
14447         this.urlAPI = (window.createObjectURL && window) || 
14448                                 (window.URL && URL.revokeObjectURL && URL) || 
14449                                 (window.webkitURL && webkitURL);
14450                         
14451          
14452          
14453          
14454         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14455         
14456         this.selectorEl.on('change', this.onFileSelected, this);
14457         if (this.images) {
14458             var t = this;
14459             this.images.forEach(function(img) {
14460                 t.addCard(img)
14461             });
14462             this.images = false;
14463         }
14464         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14465          
14466        
14467     },
14468     
14469    
14470     onClick : function(e)
14471     {
14472         e.preventDefault();
14473          
14474         this.selectorEl.dom.click();
14475          
14476     },
14477     
14478     onFileSelected : function(e)
14479     {
14480         e.preventDefault();
14481         
14482         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14483             return;
14484         }
14485         
14486         Roo.each(this.selectorEl.dom.files, function(file){    
14487             this.addFile(file);
14488         }, this);
14489          
14490     },
14491     
14492       
14493     
14494       
14495     
14496     addFile : function(file)
14497     {
14498            
14499         if(typeof(file) === 'string'){
14500             throw "Add file by name?"; // should not happen
14501             return;
14502         }
14503         
14504         if(!file || !this.urlAPI){
14505             return;
14506         }
14507         
14508         // file;
14509         // file.type;
14510         
14511         var _this = this;
14512         
14513         
14514         var url = _this.urlAPI.createObjectURL( file);
14515            
14516         this.addCard({
14517             id : Roo.bootstrap.form.CardUploader.ID--,
14518             is_uploaded : false,
14519             src : url,
14520             srcfile : file,
14521             title : file.name,
14522             mimetype : file.type,
14523             preview : false,
14524             is_deleted : 0
14525         });
14526         
14527     },
14528     
14529     /**
14530      * addCard - add an Attachment to the uploader
14531      * @param data - the data about the image to upload
14532      *
14533      * {
14534           id : 123
14535           title : "Title of file",
14536           is_uploaded : false,
14537           src : "http://.....",
14538           srcfile : { the File upload object },
14539           mimetype : file.type,
14540           preview : false,
14541           is_deleted : 0
14542           .. any other data...
14543         }
14544      *
14545      * 
14546     */
14547     
14548     addCard : function (data)
14549     {
14550         // hidden input element?
14551         // if the file is not an image...
14552         //then we need to use something other that and header_image
14553         var t = this;
14554         //   remove.....
14555         var footer = [
14556             {
14557                 xns : Roo.bootstrap,
14558                 xtype : 'CardFooter',
14559                  items: [
14560                     {
14561                         xns : Roo.bootstrap,
14562                         xtype : 'Element',
14563                         cls : 'd-flex',
14564                         items : [
14565                             
14566                             {
14567                                 xns : Roo.bootstrap,
14568                                 xtype : 'Button',
14569                                 html : String.format("<small>{0}</small>", data.title),
14570                                 cls : 'col-10 text-left',
14571                                 size: 'sm',
14572                                 weight: 'link',
14573                                 fa : 'download',
14574                                 listeners : {
14575                                     click : function() {
14576                                      
14577                                         t.fireEvent( "download", t, data );
14578                                     }
14579                                 }
14580                             },
14581                           
14582                             {
14583                                 xns : Roo.bootstrap,
14584                                 xtype : 'Button',
14585                                 style: 'max-height: 28px; ',
14586                                 size : 'sm',
14587                                 weight: 'danger',
14588                                 cls : 'col-2',
14589                                 fa : 'times',
14590                                 listeners : {
14591                                     click : function() {
14592                                         t.removeCard(data.id)
14593                                     }
14594                                 }
14595                             }
14596                         ]
14597                     }
14598                     
14599                 ] 
14600             }
14601             
14602         ];
14603         
14604         var cn = this.addxtype(
14605             {
14606                  
14607                 xns : Roo.bootstrap,
14608                 xtype : 'Card',
14609                 closeable : true,
14610                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14611                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14612                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14613                 data : data,
14614                 html : false,
14615                  
14616                 items : footer,
14617                 initEvents : function() {
14618                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14619                     var card = this;
14620                     this.imgEl = this.el.select('.card-img-top').first();
14621                     if (this.imgEl) {
14622                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14623                         this.imgEl.set({ 'pointer' : 'cursor' });
14624                                   
14625                     }
14626                     this.getCardFooter().addClass('p-1');
14627                     
14628                   
14629                 }
14630                 
14631             }
14632         );
14633         // dont' really need ot update items.
14634         // this.items.push(cn);
14635         this.fileCollection.add(cn);
14636         
14637         if (!data.srcfile) {
14638             this.updateInput();
14639             return;
14640         }
14641             
14642         var _t = this;
14643         var reader = new FileReader();
14644         reader.addEventListener("load", function() {  
14645             data.srcdata =  reader.result;
14646             _t.updateInput();
14647         });
14648         reader.readAsDataURL(data.srcfile);
14649         
14650         
14651         
14652     },
14653     removeCard : function(id)
14654     {
14655         
14656         var card  = this.fileCollection.get(id);
14657         card.data.is_deleted = 1;
14658         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14659         //this.fileCollection.remove(card);
14660         //this.items = this.items.filter(function(e) { return e != card });
14661         // dont' really need ot update items.
14662         card.el.dom.parentNode.removeChild(card.el.dom);
14663         this.updateInput();
14664
14665         
14666     },
14667     reset: function()
14668     {
14669         this.fileCollection.each(function(card) {
14670             if (card.el.dom && card.el.dom.parentNode) {
14671                 card.el.dom.parentNode.removeChild(card.el.dom);
14672             }
14673         });
14674         this.fileCollection.clear();
14675         this.updateInput();
14676     },
14677     
14678     updateInput : function()
14679     {
14680          var data = [];
14681         this.fileCollection.each(function(e) {
14682             data.push(e.data);
14683             
14684         });
14685         this.inputEl().dom.value = JSON.stringify(data);
14686         
14687         
14688         
14689     }
14690     
14691     
14692 });
14693
14694
14695 Roo.bootstrap.form.CardUploader.ID = -1;/*
14696  * Based on:
14697  * Ext JS Library 1.1.1
14698  * Copyright(c) 2006-2007, Ext JS, LLC.
14699  *
14700  * Originally Released Under LGPL - original licence link has changed is not relivant.
14701  *
14702  * Fork - LGPL
14703  * <script type="text/javascript">
14704  */
14705
14706
14707 /**
14708  * @class Roo.data.SortTypes
14709  * @static
14710  * Defines the default sorting (casting?) comparison functions used when sorting data.
14711  */
14712 Roo.data.SortTypes = {
14713     /**
14714      * Default sort that does nothing
14715      * @param {Mixed} s The value being converted
14716      * @return {Mixed} The comparison value
14717      */
14718     none : function(s){
14719         return s;
14720     },
14721     
14722     /**
14723      * The regular expression used to strip tags
14724      * @type {RegExp}
14725      * @property
14726      */
14727     stripTagsRE : /<\/?[^>]+>/gi,
14728     
14729     /**
14730      * Strips all HTML tags to sort on text only
14731      * @param {Mixed} s The value being converted
14732      * @return {String} The comparison value
14733      */
14734     asText : function(s){
14735         return String(s).replace(this.stripTagsRE, "");
14736     },
14737     
14738     /**
14739      * Strips all HTML tags to sort on text only - Case insensitive
14740      * @param {Mixed} s The value being converted
14741      * @return {String} The comparison value
14742      */
14743     asUCText : function(s){
14744         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14745     },
14746     
14747     /**
14748      * Case insensitive string
14749      * @param {Mixed} s The value being converted
14750      * @return {String} The comparison value
14751      */
14752     asUCString : function(s) {
14753         return String(s).toUpperCase();
14754     },
14755     
14756     /**
14757      * Date sorting
14758      * @param {Mixed} s The value being converted
14759      * @return {Number} The comparison value
14760      */
14761     asDate : function(s) {
14762         if(!s){
14763             return 0;
14764         }
14765         if(s instanceof Date){
14766             return s.getTime();
14767         }
14768         return Date.parse(String(s));
14769     },
14770     
14771     /**
14772      * Float sorting
14773      * @param {Mixed} s The value being converted
14774      * @return {Float} The comparison value
14775      */
14776     asFloat : function(s) {
14777         var val = parseFloat(String(s).replace(/,/g, ""));
14778         if(isNaN(val)) {
14779             val = 0;
14780         }
14781         return val;
14782     },
14783     
14784     /**
14785      * Integer sorting
14786      * @param {Mixed} s The value being converted
14787      * @return {Number} The comparison value
14788      */
14789     asInt : function(s) {
14790         var val = parseInt(String(s).replace(/,/g, ""));
14791         if(isNaN(val)) {
14792             val = 0;
14793         }
14794         return val;
14795     }
14796 };/*
14797  * Based on:
14798  * Ext JS Library 1.1.1
14799  * Copyright(c) 2006-2007, Ext JS, LLC.
14800  *
14801  * Originally Released Under LGPL - original licence link has changed is not relivant.
14802  *
14803  * Fork - LGPL
14804  * <script type="text/javascript">
14805  */
14806
14807 /**
14808 * @class Roo.data.Record
14809  * Instances of this class encapsulate both record <em>definition</em> information, and record
14810  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14811  * to access Records cached in an {@link Roo.data.Store} object.<br>
14812  * <p>
14813  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14814  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14815  * objects.<br>
14816  * <p>
14817  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14818  * @constructor
14819  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14820  * {@link #create}. The parameters are the same.
14821  * @param {Array} data An associative Array of data values keyed by the field name.
14822  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14823  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14824  * not specified an integer id is generated.
14825  */
14826 Roo.data.Record = function(data, id){
14827     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14828     this.data = data;
14829 };
14830
14831 /**
14832  * Generate a constructor for a specific record layout.
14833  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14834  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14835  * Each field definition object may contain the following properties: <ul>
14836  * <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,
14837  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14838  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14839  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14840  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14841  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14842  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14843  * this may be omitted.</p></li>
14844  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14845  * <ul><li>auto (Default, implies no conversion)</li>
14846  * <li>string</li>
14847  * <li>int</li>
14848  * <li>float</li>
14849  * <li>boolean</li>
14850  * <li>date</li></ul></p></li>
14851  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14852  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14853  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14854  * by the Reader into an object that will be stored in the Record. It is passed the
14855  * following parameters:<ul>
14856  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14857  * </ul></p></li>
14858  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14859  * </ul>
14860  * <br>usage:<br><pre><code>
14861 var TopicRecord = Roo.data.Record.create(
14862     {name: 'title', mapping: 'topic_title'},
14863     {name: 'author', mapping: 'username'},
14864     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14865     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14866     {name: 'lastPoster', mapping: 'user2'},
14867     {name: 'excerpt', mapping: 'post_text'}
14868 );
14869
14870 var myNewRecord = new TopicRecord({
14871     title: 'Do my job please',
14872     author: 'noobie',
14873     totalPosts: 1,
14874     lastPost: new Date(),
14875     lastPoster: 'Animal',
14876     excerpt: 'No way dude!'
14877 });
14878 myStore.add(myNewRecord);
14879 </code></pre>
14880  * @method create
14881  * @static
14882  */
14883 Roo.data.Record.create = function(o){
14884     var f = function(){
14885         f.superclass.constructor.apply(this, arguments);
14886     };
14887     Roo.extend(f, Roo.data.Record);
14888     var p = f.prototype;
14889     p.fields = new Roo.util.MixedCollection(false, function(field){
14890         return field.name;
14891     });
14892     for(var i = 0, len = o.length; i < len; i++){
14893         p.fields.add(new Roo.data.Field(o[i]));
14894     }
14895     f.getField = function(name){
14896         return p.fields.get(name);  
14897     };
14898     return f;
14899 };
14900
14901 Roo.data.Record.AUTO_ID = 1000;
14902 Roo.data.Record.EDIT = 'edit';
14903 Roo.data.Record.REJECT = 'reject';
14904 Roo.data.Record.COMMIT = 'commit';
14905
14906 Roo.data.Record.prototype = {
14907     /**
14908      * Readonly flag - true if this record has been modified.
14909      * @type Boolean
14910      */
14911     dirty : false,
14912     editing : false,
14913     error: null,
14914     modified: null,
14915
14916     // private
14917     join : function(store){
14918         this.store = store;
14919     },
14920
14921     /**
14922      * Set the named field to the specified value.
14923      * @param {String} name The name of the field to set.
14924      * @param {Object} value The value to set the field to.
14925      */
14926     set : function(name, value){
14927         if(this.data[name] == value){
14928             return;
14929         }
14930         this.dirty = true;
14931         if(!this.modified){
14932             this.modified = {};
14933         }
14934         if(typeof this.modified[name] == 'undefined'){
14935             this.modified[name] = this.data[name];
14936         }
14937         this.data[name] = value;
14938         if(!this.editing && this.store){
14939             this.store.afterEdit(this);
14940         }       
14941     },
14942
14943     /**
14944      * Get the value of the named field.
14945      * @param {String} name The name of the field to get the value of.
14946      * @return {Object} The value of the field.
14947      */
14948     get : function(name){
14949         return this.data[name]; 
14950     },
14951
14952     // private
14953     beginEdit : function(){
14954         this.editing = true;
14955         this.modified = {}; 
14956     },
14957
14958     // private
14959     cancelEdit : function(){
14960         this.editing = false;
14961         delete this.modified;
14962     },
14963
14964     // private
14965     endEdit : function(){
14966         this.editing = false;
14967         if(this.dirty && this.store){
14968             this.store.afterEdit(this);
14969         }
14970     },
14971
14972     /**
14973      * Usually called by the {@link Roo.data.Store} which owns the Record.
14974      * Rejects all changes made to the Record since either creation, or the last commit operation.
14975      * Modified fields are reverted to their original values.
14976      * <p>
14977      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14978      * of reject operations.
14979      */
14980     reject : function(){
14981         var m = this.modified;
14982         for(var n in m){
14983             if(typeof m[n] != "function"){
14984                 this.data[n] = m[n];
14985             }
14986         }
14987         this.dirty = false;
14988         delete this.modified;
14989         this.editing = false;
14990         if(this.store){
14991             this.store.afterReject(this);
14992         }
14993     },
14994
14995     /**
14996      * Usually called by the {@link Roo.data.Store} which owns the Record.
14997      * Commits all changes made to the Record since either creation, or the last commit operation.
14998      * <p>
14999      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15000      * of commit operations.
15001      */
15002     commit : function(){
15003         this.dirty = false;
15004         delete this.modified;
15005         this.editing = false;
15006         if(this.store){
15007             this.store.afterCommit(this);
15008         }
15009     },
15010
15011     // private
15012     hasError : function(){
15013         return this.error != null;
15014     },
15015
15016     // private
15017     clearError : function(){
15018         this.error = null;
15019     },
15020
15021     /**
15022      * Creates a copy of this record.
15023      * @param {String} id (optional) A new record id if you don't want to use this record's id
15024      * @return {Record}
15025      */
15026     copy : function(newId) {
15027         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15028     }
15029 };/*
15030  * Based on:
15031  * Ext JS Library 1.1.1
15032  * Copyright(c) 2006-2007, Ext JS, LLC.
15033  *
15034  * Originally Released Under LGPL - original licence link has changed is not relivant.
15035  *
15036  * Fork - LGPL
15037  * <script type="text/javascript">
15038  */
15039
15040
15041
15042 /**
15043  * @class Roo.data.Store
15044  * @extends Roo.util.Observable
15045  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15046  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15047  * <p>
15048  * 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
15049  * has no knowledge of the format of the data returned by the Proxy.<br>
15050  * <p>
15051  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15052  * instances from the data object. These records are cached and made available through accessor functions.
15053  * @constructor
15054  * Creates a new Store.
15055  * @param {Object} config A config object containing the objects needed for the Store to access data,
15056  * and read the data into Records.
15057  */
15058 Roo.data.Store = function(config){
15059     this.data = new Roo.util.MixedCollection(false);
15060     this.data.getKey = function(o){
15061         return o.id;
15062     };
15063     this.baseParams = {};
15064     // private
15065     this.paramNames = {
15066         "start" : "start",
15067         "limit" : "limit",
15068         "sort" : "sort",
15069         "dir" : "dir",
15070         "multisort" : "_multisort"
15071     };
15072
15073     if(config && config.data){
15074         this.inlineData = config.data;
15075         delete config.data;
15076     }
15077
15078     Roo.apply(this, config);
15079     
15080     if(this.reader){ // reader passed
15081         this.reader = Roo.factory(this.reader, Roo.data);
15082         this.reader.xmodule = this.xmodule || false;
15083         if(!this.recordType){
15084             this.recordType = this.reader.recordType;
15085         }
15086         if(this.reader.onMetaChange){
15087             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15088         }
15089     }
15090
15091     if(this.recordType){
15092         this.fields = this.recordType.prototype.fields;
15093     }
15094     this.modified = [];
15095
15096     this.addEvents({
15097         /**
15098          * @event datachanged
15099          * Fires when the data cache has changed, and a widget which is using this Store
15100          * as a Record cache should refresh its view.
15101          * @param {Store} this
15102          */
15103         datachanged : true,
15104         /**
15105          * @event metachange
15106          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15107          * @param {Store} this
15108          * @param {Object} meta The JSON metadata
15109          */
15110         metachange : true,
15111         /**
15112          * @event add
15113          * Fires when Records have been added to the Store
15114          * @param {Store} this
15115          * @param {Roo.data.Record[]} records The array of Records added
15116          * @param {Number} index The index at which the record(s) were added
15117          */
15118         add : true,
15119         /**
15120          * @event remove
15121          * Fires when a Record has been removed from the Store
15122          * @param {Store} this
15123          * @param {Roo.data.Record} record The Record that was removed
15124          * @param {Number} index The index at which the record was removed
15125          */
15126         remove : true,
15127         /**
15128          * @event update
15129          * Fires when a Record has been updated
15130          * @param {Store} this
15131          * @param {Roo.data.Record} record The Record that was updated
15132          * @param {String} operation The update operation being performed.  Value may be one of:
15133          * <pre><code>
15134  Roo.data.Record.EDIT
15135  Roo.data.Record.REJECT
15136  Roo.data.Record.COMMIT
15137          * </code></pre>
15138          */
15139         update : true,
15140         /**
15141          * @event clear
15142          * Fires when the data cache has been cleared.
15143          * @param {Store} this
15144          */
15145         clear : true,
15146         /**
15147          * @event beforeload
15148          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15149          * the load action will be canceled.
15150          * @param {Store} this
15151          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15152          */
15153         beforeload : true,
15154         /**
15155          * @event beforeloadadd
15156          * Fires after a new set of Records has been loaded.
15157          * @param {Store} this
15158          * @param {Roo.data.Record[]} records The Records that were loaded
15159          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15160          */
15161         beforeloadadd : true,
15162         /**
15163          * @event load
15164          * Fires after a new set of Records has been loaded, before they are added to the store.
15165          * @param {Store} this
15166          * @param {Roo.data.Record[]} records The Records that were loaded
15167          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15168          * @params {Object} return from reader
15169          */
15170         load : true,
15171         /**
15172          * @event loadexception
15173          * Fires if an exception occurs in the Proxy during loading.
15174          * Called with the signature of the Proxy's "loadexception" event.
15175          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15176          * 
15177          * @param {Proxy} 
15178          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15179          * @param {Object} load options 
15180          * @param {Object} jsonData from your request (normally this contains the Exception)
15181          */
15182         loadexception : true
15183     });
15184     
15185     if(this.proxy){
15186         this.proxy = Roo.factory(this.proxy, Roo.data);
15187         this.proxy.xmodule = this.xmodule || false;
15188         this.relayEvents(this.proxy,  ["loadexception"]);
15189     }
15190     this.sortToggle = {};
15191     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15192
15193     Roo.data.Store.superclass.constructor.call(this);
15194
15195     if(this.inlineData){
15196         this.loadData(this.inlineData);
15197         delete this.inlineData;
15198     }
15199 };
15200
15201 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15202      /**
15203     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15204     * without a remote query - used by combo/forms at present.
15205     */
15206     
15207     /**
15208     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15209     */
15210     /**
15211     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15212     */
15213     /**
15214     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15215     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15216     */
15217     /**
15218     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15219     * on any HTTP request
15220     */
15221     /**
15222     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15223     */
15224     /**
15225     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15226     */
15227     multiSort: false,
15228     /**
15229     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15230     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15231     */
15232     remoteSort : false,
15233
15234     /**
15235     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15236      * loaded or when a record is removed. (defaults to false).
15237     */
15238     pruneModifiedRecords : false,
15239
15240     // private
15241     lastOptions : null,
15242
15243     /**
15244      * Add Records to the Store and fires the add event.
15245      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15246      */
15247     add : function(records){
15248         records = [].concat(records);
15249         for(var i = 0, len = records.length; i < len; i++){
15250             records[i].join(this);
15251         }
15252         var index = this.data.length;
15253         this.data.addAll(records);
15254         this.fireEvent("add", this, records, index);
15255     },
15256
15257     /**
15258      * Remove a Record from the Store and fires the remove event.
15259      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15260      */
15261     remove : function(record){
15262         var index = this.data.indexOf(record);
15263         this.data.removeAt(index);
15264  
15265         if(this.pruneModifiedRecords){
15266             this.modified.remove(record);
15267         }
15268         this.fireEvent("remove", this, record, index);
15269     },
15270
15271     /**
15272      * Remove all Records from the Store and fires the clear event.
15273      */
15274     removeAll : function(){
15275         this.data.clear();
15276         if(this.pruneModifiedRecords){
15277             this.modified = [];
15278         }
15279         this.fireEvent("clear", this);
15280     },
15281
15282     /**
15283      * Inserts Records to the Store at the given index and fires the add event.
15284      * @param {Number} index The start index at which to insert the passed Records.
15285      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15286      */
15287     insert : function(index, records){
15288         records = [].concat(records);
15289         for(var i = 0, len = records.length; i < len; i++){
15290             this.data.insert(index, records[i]);
15291             records[i].join(this);
15292         }
15293         this.fireEvent("add", this, records, index);
15294     },
15295
15296     /**
15297      * Get the index within the cache of the passed Record.
15298      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15299      * @return {Number} The index of the passed Record. Returns -1 if not found.
15300      */
15301     indexOf : function(record){
15302         return this.data.indexOf(record);
15303     },
15304
15305     /**
15306      * Get the index within the cache of the Record with the passed id.
15307      * @param {String} id The id of the Record to find.
15308      * @return {Number} The index of the Record. Returns -1 if not found.
15309      */
15310     indexOfId : function(id){
15311         return this.data.indexOfKey(id);
15312     },
15313
15314     /**
15315      * Get the Record with the specified id.
15316      * @param {String} id The id of the Record to find.
15317      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15318      */
15319     getById : function(id){
15320         return this.data.key(id);
15321     },
15322
15323     /**
15324      * Get the Record at the specified index.
15325      * @param {Number} index The index of the Record to find.
15326      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15327      */
15328     getAt : function(index){
15329         return this.data.itemAt(index);
15330     },
15331
15332     /**
15333      * Returns a range of Records between specified indices.
15334      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15335      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15336      * @return {Roo.data.Record[]} An array of Records
15337      */
15338     getRange : function(start, end){
15339         return this.data.getRange(start, end);
15340     },
15341
15342     // private
15343     storeOptions : function(o){
15344         o = Roo.apply({}, o);
15345         delete o.callback;
15346         delete o.scope;
15347         this.lastOptions = o;
15348     },
15349
15350     /**
15351      * Loads the Record cache from the configured Proxy using the configured Reader.
15352      * <p>
15353      * If using remote paging, then the first load call must specify the <em>start</em>
15354      * and <em>limit</em> properties in the options.params property to establish the initial
15355      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15356      * <p>
15357      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15358      * and this call will return before the new data has been loaded. Perform any post-processing
15359      * in a callback function, or in a "load" event handler.</strong>
15360      * <p>
15361      * @param {Object} options An object containing properties which control loading options:<ul>
15362      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15363      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15364      * <pre>
15365                 {
15366                     data : data,  // array of key=>value data like JsonReader
15367                     total : data.length,
15368                     success : true
15369                     
15370                 }
15371         </pre>
15372             }.</li>
15373      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15374      * passed the following arguments:<ul>
15375      * <li>r : Roo.data.Record[]</li>
15376      * <li>options: Options object from the load call</li>
15377      * <li>success: Boolean success indicator</li></ul></li>
15378      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15379      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15380      * </ul>
15381      */
15382     load : function(options){
15383         options = options || {};
15384         if(this.fireEvent("beforeload", this, options) !== false){
15385             this.storeOptions(options);
15386             var p = Roo.apply(options.params || {}, this.baseParams);
15387             // if meta was not loaded from remote source.. try requesting it.
15388             if (!this.reader.metaFromRemote) {
15389                 p._requestMeta = 1;
15390             }
15391             if(this.sortInfo && this.remoteSort){
15392                 var pn = this.paramNames;
15393                 p[pn["sort"]] = this.sortInfo.field;
15394                 p[pn["dir"]] = this.sortInfo.direction;
15395             }
15396             if (this.multiSort) {
15397                 var pn = this.paramNames;
15398                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15399             }
15400             
15401             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15402         }
15403     },
15404
15405     /**
15406      * Reloads the Record cache from the configured Proxy using the configured Reader and
15407      * the options from the last load operation performed.
15408      * @param {Object} options (optional) An object containing properties which may override the options
15409      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15410      * the most recently used options are reused).
15411      */
15412     reload : function(options){
15413         this.load(Roo.applyIf(options||{}, this.lastOptions));
15414     },
15415
15416     // private
15417     // Called as a callback by the Reader during a load operation.
15418     loadRecords : function(o, options, success){
15419          
15420         if(!o){
15421             if(success !== false){
15422                 this.fireEvent("load", this, [], options, o);
15423             }
15424             if(options.callback){
15425                 options.callback.call(options.scope || this, [], options, false);
15426             }
15427             return;
15428         }
15429         // if data returned failure - throw an exception.
15430         if (o.success === false) {
15431             // show a message if no listener is registered.
15432             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15433                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15434             }
15435             // loadmask wil be hooked into this..
15436             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15437             return;
15438         }
15439         var r = o.records, t = o.totalRecords || r.length;
15440         
15441         this.fireEvent("beforeloadadd", this, r, options, o);
15442         
15443         if(!options || options.add !== true){
15444             if(this.pruneModifiedRecords){
15445                 this.modified = [];
15446             }
15447             for(var i = 0, len = r.length; i < len; i++){
15448                 r[i].join(this);
15449             }
15450             if(this.snapshot){
15451                 this.data = this.snapshot;
15452                 delete this.snapshot;
15453             }
15454             this.data.clear();
15455             this.data.addAll(r);
15456             this.totalLength = t;
15457             this.applySort();
15458             this.fireEvent("datachanged", this);
15459         }else{
15460             this.totalLength = Math.max(t, this.data.length+r.length);
15461             this.add(r);
15462         }
15463         
15464         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15465                 
15466             var e = new Roo.data.Record({});
15467
15468             e.set(this.parent.displayField, this.parent.emptyTitle);
15469             e.set(this.parent.valueField, '');
15470
15471             this.insert(0, e);
15472         }
15473             
15474         this.fireEvent("load", this, r, options, o);
15475         if(options.callback){
15476             options.callback.call(options.scope || this, r, options, true);
15477         }
15478     },
15479
15480
15481     /**
15482      * Loads data from a passed data block. A Reader which understands the format of the data
15483      * must have been configured in the constructor.
15484      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15485      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15486      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15487      */
15488     loadData : function(o, append){
15489         var r = this.reader.readRecords(o);
15490         this.loadRecords(r, {add: append}, true);
15491     },
15492     
15493      /**
15494      * using 'cn' the nested child reader read the child array into it's child stores.
15495      * @param {Object} rec The record with a 'children array
15496      */
15497     loadDataFromChildren : function(rec)
15498     {
15499         this.loadData(this.reader.toLoadData(rec));
15500     },
15501     
15502
15503     /**
15504      * Gets the number of cached records.
15505      * <p>
15506      * <em>If using paging, this may not be the total size of the dataset. If the data object
15507      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15508      * the data set size</em>
15509      */
15510     getCount : function(){
15511         return this.data.length || 0;
15512     },
15513
15514     /**
15515      * Gets the total number of records in the dataset as returned by the server.
15516      * <p>
15517      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15518      * the dataset size</em>
15519      */
15520     getTotalCount : function(){
15521         return this.totalLength || 0;
15522     },
15523
15524     /**
15525      * Returns the sort state of the Store as an object with two properties:
15526      * <pre><code>
15527  field {String} The name of the field by which the Records are sorted
15528  direction {String} The sort order, "ASC" or "DESC"
15529      * </code></pre>
15530      */
15531     getSortState : function(){
15532         return this.sortInfo;
15533     },
15534
15535     // private
15536     applySort : function(){
15537         if(this.sortInfo && !this.remoteSort){
15538             var s = this.sortInfo, f = s.field;
15539             var st = this.fields.get(f).sortType;
15540             var fn = function(r1, r2){
15541                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15542                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15543             };
15544             this.data.sort(s.direction, fn);
15545             if(this.snapshot && this.snapshot != this.data){
15546                 this.snapshot.sort(s.direction, fn);
15547             }
15548         }
15549     },
15550
15551     /**
15552      * Sets the default sort column and order to be used by the next load operation.
15553      * @param {String} fieldName The name of the field to sort by.
15554      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15555      */
15556     setDefaultSort : function(field, dir){
15557         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15558     },
15559
15560     /**
15561      * Sort the Records.
15562      * If remote sorting is used, the sort is performed on the server, and the cache is
15563      * reloaded. If local sorting is used, the cache is sorted internally.
15564      * @param {String} fieldName The name of the field to sort by.
15565      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15566      */
15567     sort : function(fieldName, dir){
15568         var f = this.fields.get(fieldName);
15569         if(!dir){
15570             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15571             
15572             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15573                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15574             }else{
15575                 dir = f.sortDir;
15576             }
15577         }
15578         this.sortToggle[f.name] = dir;
15579         this.sortInfo = {field: f.name, direction: dir};
15580         if(!this.remoteSort){
15581             this.applySort();
15582             this.fireEvent("datachanged", this);
15583         }else{
15584             this.load(this.lastOptions);
15585         }
15586     },
15587
15588     /**
15589      * Calls the specified function for each of the Records in the cache.
15590      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15591      * Returning <em>false</em> aborts and exits the iteration.
15592      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15593      */
15594     each : function(fn, scope){
15595         this.data.each(fn, scope);
15596     },
15597
15598     /**
15599      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15600      * (e.g., during paging).
15601      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15602      */
15603     getModifiedRecords : function(){
15604         return this.modified;
15605     },
15606
15607     // private
15608     createFilterFn : function(property, value, anyMatch){
15609         if(!value.exec){ // not a regex
15610             value = String(value);
15611             if(value.length == 0){
15612                 return false;
15613             }
15614             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15615         }
15616         return function(r){
15617             return value.test(r.data[property]);
15618         };
15619     },
15620
15621     /**
15622      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15623      * @param {String} property A field on your records
15624      * @param {Number} start The record index to start at (defaults to 0)
15625      * @param {Number} end The last record index to include (defaults to length - 1)
15626      * @return {Number} The sum
15627      */
15628     sum : function(property, start, end){
15629         var rs = this.data.items, v = 0;
15630         start = start || 0;
15631         end = (end || end === 0) ? end : rs.length-1;
15632
15633         for(var i = start; i <= end; i++){
15634             v += (rs[i].data[property] || 0);
15635         }
15636         return v;
15637     },
15638
15639     /**
15640      * Filter the records by a specified property.
15641      * @param {String} field A field on your records
15642      * @param {String/RegExp} value Either a string that the field
15643      * should start with or a RegExp to test against the field
15644      * @param {Boolean} anyMatch True to match any part not just the beginning
15645      */
15646     filter : function(property, value, anyMatch){
15647         var fn = this.createFilterFn(property, value, anyMatch);
15648         return fn ? this.filterBy(fn) : this.clearFilter();
15649     },
15650
15651     /**
15652      * Filter by a function. The specified function will be called with each
15653      * record in this data source. If the function returns true the record is included,
15654      * otherwise it is filtered.
15655      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15656      * @param {Object} scope (optional) The scope of the function (defaults to this)
15657      */
15658     filterBy : function(fn, scope){
15659         this.snapshot = this.snapshot || this.data;
15660         this.data = this.queryBy(fn, scope||this);
15661         this.fireEvent("datachanged", this);
15662     },
15663
15664     /**
15665      * Query the records by a specified property.
15666      * @param {String} field A field on your records
15667      * @param {String/RegExp} value Either a string that the field
15668      * should start with or a RegExp to test against the field
15669      * @param {Boolean} anyMatch True to match any part not just the beginning
15670      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15671      */
15672     query : function(property, value, anyMatch){
15673         var fn = this.createFilterFn(property, value, anyMatch);
15674         return fn ? this.queryBy(fn) : this.data.clone();
15675     },
15676
15677     /**
15678      * Query by a function. The specified function will be called with each
15679      * record in this data source. If the function returns true the record is included
15680      * in the results.
15681      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15682      * @param {Object} scope (optional) The scope of the function (defaults to this)
15683       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15684      **/
15685     queryBy : function(fn, scope){
15686         var data = this.snapshot || this.data;
15687         return data.filterBy(fn, scope||this);
15688     },
15689
15690     /**
15691      * Collects unique values for a particular dataIndex from this store.
15692      * @param {String} dataIndex The property to collect
15693      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15694      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15695      * @return {Array} An array of the unique values
15696      **/
15697     collect : function(dataIndex, allowNull, bypassFilter){
15698         var d = (bypassFilter === true && this.snapshot) ?
15699                 this.snapshot.items : this.data.items;
15700         var v, sv, r = [], l = {};
15701         for(var i = 0, len = d.length; i < len; i++){
15702             v = d[i].data[dataIndex];
15703             sv = String(v);
15704             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15705                 l[sv] = true;
15706                 r[r.length] = v;
15707             }
15708         }
15709         return r;
15710     },
15711
15712     /**
15713      * Revert to a view of the Record cache with no filtering applied.
15714      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15715      */
15716     clearFilter : function(suppressEvent){
15717         if(this.snapshot && this.snapshot != this.data){
15718             this.data = this.snapshot;
15719             delete this.snapshot;
15720             if(suppressEvent !== true){
15721                 this.fireEvent("datachanged", this);
15722             }
15723         }
15724     },
15725
15726     // private
15727     afterEdit : function(record){
15728         if(this.modified.indexOf(record) == -1){
15729             this.modified.push(record);
15730         }
15731         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15732     },
15733     
15734     // private
15735     afterReject : function(record){
15736         this.modified.remove(record);
15737         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15738     },
15739
15740     // private
15741     afterCommit : function(record){
15742         this.modified.remove(record);
15743         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15744     },
15745
15746     /**
15747      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15748      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15749      */
15750     commitChanges : function(){
15751         var m = this.modified.slice(0);
15752         this.modified = [];
15753         for(var i = 0, len = m.length; i < len; i++){
15754             m[i].commit();
15755         }
15756     },
15757
15758     /**
15759      * Cancel outstanding changes on all changed records.
15760      */
15761     rejectChanges : function(){
15762         var m = this.modified.slice(0);
15763         this.modified = [];
15764         for(var i = 0, len = m.length; i < len; i++){
15765             m[i].reject();
15766         }
15767     },
15768
15769     onMetaChange : function(meta, rtype, o){
15770         this.recordType = rtype;
15771         this.fields = rtype.prototype.fields;
15772         delete this.snapshot;
15773         this.sortInfo = meta.sortInfo || this.sortInfo;
15774         this.modified = [];
15775         this.fireEvent('metachange', this, this.reader.meta);
15776     },
15777     
15778     moveIndex : function(data, type)
15779     {
15780         var index = this.indexOf(data);
15781         
15782         var newIndex = index + type;
15783         
15784         this.remove(data);
15785         
15786         this.insert(newIndex, data);
15787         
15788     }
15789 });/*
15790  * Based on:
15791  * Ext JS Library 1.1.1
15792  * Copyright(c) 2006-2007, Ext JS, LLC.
15793  *
15794  * Originally Released Under LGPL - original licence link has changed is not relivant.
15795  *
15796  * Fork - LGPL
15797  * <script type="text/javascript">
15798  */
15799
15800 /**
15801  * @class Roo.data.SimpleStore
15802  * @extends Roo.data.Store
15803  * Small helper class to make creating Stores from Array data easier.
15804  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15805  * @cfg {Array} fields An array of field definition objects, or field name strings.
15806  * @cfg {Object} an existing reader (eg. copied from another store)
15807  * @cfg {Array} data The multi-dimensional array of data
15808  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15809  * @cfg {Roo.data.Reader} reader  [not-required] 
15810  * @constructor
15811  * @param {Object} config
15812  */
15813 Roo.data.SimpleStore = function(config)
15814 {
15815     Roo.data.SimpleStore.superclass.constructor.call(this, {
15816         isLocal : true,
15817         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15818                 id: config.id
15819             },
15820             Roo.data.Record.create(config.fields)
15821         ),
15822         proxy : new Roo.data.MemoryProxy(config.data)
15823     });
15824     this.load();
15825 };
15826 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15827  * Based on:
15828  * Ext JS Library 1.1.1
15829  * Copyright(c) 2006-2007, Ext JS, LLC.
15830  *
15831  * Originally Released Under LGPL - original licence link has changed is not relivant.
15832  *
15833  * Fork - LGPL
15834  * <script type="text/javascript">
15835  */
15836
15837 /**
15838 /**
15839  * @extends Roo.data.Store
15840  * @class Roo.data.JsonStore
15841  * Small helper class to make creating Stores for JSON data easier. <br/>
15842 <pre><code>
15843 var store = new Roo.data.JsonStore({
15844     url: 'get-images.php',
15845     root: 'images',
15846     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15847 });
15848 </code></pre>
15849  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15850  * JsonReader and HttpProxy (unless inline data is provided).</b>
15851  * @cfg {Array} fields An array of field definition objects, or field name strings.
15852  * @constructor
15853  * @param {Object} config
15854  */
15855 Roo.data.JsonStore = function(c){
15856     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15857         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15858         reader: new Roo.data.JsonReader(c, c.fields)
15859     }));
15860 };
15861 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15862  * Based on:
15863  * Ext JS Library 1.1.1
15864  * Copyright(c) 2006-2007, Ext JS, LLC.
15865  *
15866  * Originally Released Under LGPL - original licence link has changed is not relivant.
15867  *
15868  * Fork - LGPL
15869  * <script type="text/javascript">
15870  */
15871
15872  
15873 Roo.data.Field = function(config){
15874     if(typeof config == "string"){
15875         config = {name: config};
15876     }
15877     Roo.apply(this, config);
15878     
15879     if(!this.type){
15880         this.type = "auto";
15881     }
15882     
15883     var st = Roo.data.SortTypes;
15884     // named sortTypes are supported, here we look them up
15885     if(typeof this.sortType == "string"){
15886         this.sortType = st[this.sortType];
15887     }
15888     
15889     // set default sortType for strings and dates
15890     if(!this.sortType){
15891         switch(this.type){
15892             case "string":
15893                 this.sortType = st.asUCString;
15894                 break;
15895             case "date":
15896                 this.sortType = st.asDate;
15897                 break;
15898             default:
15899                 this.sortType = st.none;
15900         }
15901     }
15902
15903     // define once
15904     var stripRe = /[\$,%]/g;
15905
15906     // prebuilt conversion function for this field, instead of
15907     // switching every time we're reading a value
15908     if(!this.convert){
15909         var cv, dateFormat = this.dateFormat;
15910         switch(this.type){
15911             case "":
15912             case "auto":
15913             case undefined:
15914                 cv = function(v){ return v; };
15915                 break;
15916             case "string":
15917                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15918                 break;
15919             case "int":
15920                 cv = function(v){
15921                     return v !== undefined && v !== null && v !== '' ?
15922                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15923                     };
15924                 break;
15925             case "float":
15926                 cv = function(v){
15927                     return v !== undefined && v !== null && v !== '' ?
15928                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15929                     };
15930                 break;
15931             case "bool":
15932             case "boolean":
15933                 cv = function(v){ return v === true || v === "true" || v == 1; };
15934                 break;
15935             case "date":
15936                 cv = function(v){
15937                     if(!v){
15938                         return '';
15939                     }
15940                     if(v instanceof Date){
15941                         return v;
15942                     }
15943                     if(dateFormat){
15944                         if(dateFormat == "timestamp"){
15945                             return new Date(v*1000);
15946                         }
15947                         return Date.parseDate(v, dateFormat);
15948                     }
15949                     var parsed = Date.parse(v);
15950                     return parsed ? new Date(parsed) : null;
15951                 };
15952              break;
15953             
15954         }
15955         this.convert = cv;
15956     }
15957 };
15958
15959 Roo.data.Field.prototype = {
15960     dateFormat: null,
15961     defaultValue: "",
15962     mapping: null,
15963     sortType : null,
15964     sortDir : "ASC"
15965 };/*
15966  * Based on:
15967  * Ext JS Library 1.1.1
15968  * Copyright(c) 2006-2007, Ext JS, LLC.
15969  *
15970  * Originally Released Under LGPL - original licence link has changed is not relivant.
15971  *
15972  * Fork - LGPL
15973  * <script type="text/javascript">
15974  */
15975  
15976 // Base class for reading structured data from a data source.  This class is intended to be
15977 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15978
15979 /**
15980  * @class Roo.data.DataReader
15981  * @abstract
15982  * Base class for reading structured data from a data source.  This class is intended to be
15983  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15984  */
15985
15986 Roo.data.DataReader = function(meta, recordType){
15987     
15988     this.meta = meta;
15989     
15990     this.recordType = recordType instanceof Array ? 
15991         Roo.data.Record.create(recordType) : recordType;
15992 };
15993
15994 Roo.data.DataReader.prototype = {
15995     
15996     
15997     readerType : 'Data',
15998      /**
15999      * Create an empty record
16000      * @param {Object} data (optional) - overlay some values
16001      * @return {Roo.data.Record} record created.
16002      */
16003     newRow :  function(d) {
16004         var da =  {};
16005         this.recordType.prototype.fields.each(function(c) {
16006             switch( c.type) {
16007                 case 'int' : da[c.name] = 0; break;
16008                 case 'date' : da[c.name] = new Date(); break;
16009                 case 'float' : da[c.name] = 0.0; break;
16010                 case 'boolean' : da[c.name] = false; break;
16011                 default : da[c.name] = ""; break;
16012             }
16013             
16014         });
16015         return new this.recordType(Roo.apply(da, d));
16016     }
16017     
16018     
16019 };/*
16020  * Based on:
16021  * Ext JS Library 1.1.1
16022  * Copyright(c) 2006-2007, Ext JS, LLC.
16023  *
16024  * Originally Released Under LGPL - original licence link has changed is not relivant.
16025  *
16026  * Fork - LGPL
16027  * <script type="text/javascript">
16028  */
16029
16030 /**
16031  * @class Roo.data.DataProxy
16032  * @extends Roo.util.Observable
16033  * @abstract
16034  * This class is an abstract base class for implementations which provide retrieval of
16035  * unformatted data objects.<br>
16036  * <p>
16037  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16038  * (of the appropriate type which knows how to parse the data object) to provide a block of
16039  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16040  * <p>
16041  * Custom implementations must implement the load method as described in
16042  * {@link Roo.data.HttpProxy#load}.
16043  */
16044 Roo.data.DataProxy = function(){
16045     this.addEvents({
16046         /**
16047          * @event beforeload
16048          * Fires before a network request is made to retrieve a data object.
16049          * @param {Object} This DataProxy object.
16050          * @param {Object} params The params parameter to the load function.
16051          */
16052         beforeload : true,
16053         /**
16054          * @event load
16055          * Fires before the load method's callback is called.
16056          * @param {Object} This DataProxy object.
16057          * @param {Object} o The data object.
16058          * @param {Object} arg The callback argument object passed to the load function.
16059          */
16060         load : true,
16061         /**
16062          * @event loadexception
16063          * Fires if an Exception occurs during data retrieval.
16064          * @param {Object} This DataProxy object.
16065          * @param {Object} o The data object.
16066          * @param {Object} arg The callback argument object passed to the load function.
16067          * @param {Object} e The Exception.
16068          */
16069         loadexception : true
16070     });
16071     Roo.data.DataProxy.superclass.constructor.call(this);
16072 };
16073
16074 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16075
16076     /**
16077      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16078      */
16079 /*
16080  * Based on:
16081  * Ext JS Library 1.1.1
16082  * Copyright(c) 2006-2007, Ext JS, LLC.
16083  *
16084  * Originally Released Under LGPL - original licence link has changed is not relivant.
16085  *
16086  * Fork - LGPL
16087  * <script type="text/javascript">
16088  */
16089 /**
16090  * @class Roo.data.MemoryProxy
16091  * @extends Roo.data.DataProxy
16092  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16093  * to the Reader when its load method is called.
16094  * @constructor
16095  * @param {Object} config  A config object containing the objects needed for the Store to access data,
16096  */
16097 Roo.data.MemoryProxy = function(config){
16098     var data = config;
16099     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16100         data = config.data;
16101     }
16102     Roo.data.MemoryProxy.superclass.constructor.call(this);
16103     this.data = data;
16104 };
16105
16106 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16107     
16108     /**
16109      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16110      */
16111     /**
16112      * Load data from the requested source (in this case an in-memory
16113      * data object passed to the constructor), read the data object into
16114      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16115      * process that block using the passed callback.
16116      * @param {Object} params This parameter is not used by the MemoryProxy class.
16117      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16118      * object into a block of Roo.data.Records.
16119      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16120      * The function must be passed <ul>
16121      * <li>The Record block object</li>
16122      * <li>The "arg" argument from the load function</li>
16123      * <li>A boolean success indicator</li>
16124      * </ul>
16125      * @param {Object} scope The scope in which to call the callback
16126      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16127      */
16128     load : function(params, reader, callback, scope, arg){
16129         params = params || {};
16130         var result;
16131         try {
16132             result = reader.readRecords(params.data ? params.data :this.data);
16133         }catch(e){
16134             this.fireEvent("loadexception", this, arg, null, e);
16135             callback.call(scope, null, arg, false);
16136             return;
16137         }
16138         callback.call(scope, result, arg, true);
16139     },
16140     
16141     // private
16142     update : function(params, records){
16143         
16144     }
16145 });/*
16146  * Based on:
16147  * Ext JS Library 1.1.1
16148  * Copyright(c) 2006-2007, Ext JS, LLC.
16149  *
16150  * Originally Released Under LGPL - original licence link has changed is not relivant.
16151  *
16152  * Fork - LGPL
16153  * <script type="text/javascript">
16154  */
16155 /**
16156  * @class Roo.data.HttpProxy
16157  * @extends Roo.data.DataProxy
16158  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16159  * configured to reference a certain URL.<br><br>
16160  * <p>
16161  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16162  * from which the running page was served.<br><br>
16163  * <p>
16164  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16165  * <p>
16166  * Be aware that to enable the browser to parse an XML document, the server must set
16167  * the Content-Type header in the HTTP response to "text/xml".
16168  * @constructor
16169  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16170  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16171  * will be used to make the request.
16172  */
16173 Roo.data.HttpProxy = function(conn){
16174     Roo.data.HttpProxy.superclass.constructor.call(this);
16175     // is conn a conn config or a real conn?
16176     this.conn = conn;
16177     this.useAjax = !conn || !conn.events;
16178   
16179 };
16180
16181 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16182     // thse are take from connection...
16183     
16184     /**
16185      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16186      */
16187     /**
16188      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16189      * extra parameters to each request made by this object. (defaults to undefined)
16190      */
16191     /**
16192      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16193      *  to each request made by this object. (defaults to undefined)
16194      */
16195     /**
16196      * @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)
16197      */
16198     /**
16199      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16200      */
16201      /**
16202      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16203      * @type Boolean
16204      */
16205   
16206
16207     /**
16208      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16209      * @type Boolean
16210      */
16211     /**
16212      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16213      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16214      * a finer-grained basis than the DataProxy events.
16215      */
16216     getConnection : function(){
16217         return this.useAjax ? Roo.Ajax : this.conn;
16218     },
16219
16220     /**
16221      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16222      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16223      * process that block using the passed callback.
16224      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16225      * for the request to the remote server.
16226      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16227      * object into a block of Roo.data.Records.
16228      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16229      * The function must be passed <ul>
16230      * <li>The Record block object</li>
16231      * <li>The "arg" argument from the load function</li>
16232      * <li>A boolean success indicator</li>
16233      * </ul>
16234      * @param {Object} scope The scope in which to call the callback
16235      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16236      */
16237     load : function(params, reader, callback, scope, arg){
16238         if(this.fireEvent("beforeload", this, params) !== false){
16239             var  o = {
16240                 params : params || {},
16241                 request: {
16242                     callback : callback,
16243                     scope : scope,
16244                     arg : arg
16245                 },
16246                 reader: reader,
16247                 callback : this.loadResponse,
16248                 scope: this
16249             };
16250             if(this.useAjax){
16251                 Roo.applyIf(o, this.conn);
16252                 if(this.activeRequest){
16253                     Roo.Ajax.abort(this.activeRequest);
16254                 }
16255                 this.activeRequest = Roo.Ajax.request(o);
16256             }else{
16257                 this.conn.request(o);
16258             }
16259         }else{
16260             callback.call(scope||this, null, arg, false);
16261         }
16262     },
16263
16264     // private
16265     loadResponse : function(o, success, response){
16266         delete this.activeRequest;
16267         if(!success){
16268             this.fireEvent("loadexception", this, o, response);
16269             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16270             return;
16271         }
16272         var result;
16273         try {
16274             result = o.reader.read(response);
16275         }catch(e){
16276             o.success = false;
16277             o.raw = { errorMsg : response.responseText };
16278             this.fireEvent("loadexception", this, o, response, e);
16279             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16280             return;
16281         }
16282         
16283         this.fireEvent("load", this, o, o.request.arg);
16284         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16285     },
16286
16287     // private
16288     update : function(dataSet){
16289
16290     },
16291
16292     // private
16293     updateResponse : function(dataSet){
16294
16295     }
16296 });/*
16297  * Based on:
16298  * Ext JS Library 1.1.1
16299  * Copyright(c) 2006-2007, Ext JS, LLC.
16300  *
16301  * Originally Released Under LGPL - original licence link has changed is not relivant.
16302  *
16303  * Fork - LGPL
16304  * <script type="text/javascript">
16305  */
16306
16307 /**
16308  * @class Roo.data.ScriptTagProxy
16309  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16310  * other than the originating domain of the running page.<br><br>
16311  * <p>
16312  * <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
16313  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16314  * <p>
16315  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16316  * source code that is used as the source inside a &lt;script> tag.<br><br>
16317  * <p>
16318  * In order for the browser to process the returned data, the server must wrap the data object
16319  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16320  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16321  * depending on whether the callback name was passed:
16322  * <p>
16323  * <pre><code>
16324 boolean scriptTag = false;
16325 String cb = request.getParameter("callback");
16326 if (cb != null) {
16327     scriptTag = true;
16328     response.setContentType("text/javascript");
16329 } else {
16330     response.setContentType("application/x-json");
16331 }
16332 Writer out = response.getWriter();
16333 if (scriptTag) {
16334     out.write(cb + "(");
16335 }
16336 out.print(dataBlock.toJsonString());
16337 if (scriptTag) {
16338     out.write(");");
16339 }
16340 </pre></code>
16341  *
16342  * @constructor
16343  * @param {Object} config A configuration object.
16344  */
16345 Roo.data.ScriptTagProxy = function(config){
16346     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16347     Roo.apply(this, config);
16348     this.head = document.getElementsByTagName("head")[0];
16349 };
16350
16351 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16352
16353 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16354     /**
16355      * @cfg {String} url The URL from which to request the data object.
16356      */
16357     /**
16358      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16359      */
16360     timeout : 30000,
16361     /**
16362      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16363      * the server the name of the callback function set up by the load call to process the returned data object.
16364      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16365      * javascript output which calls this named function passing the data object as its only parameter.
16366      */
16367     callbackParam : "callback",
16368     /**
16369      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16370      * name to the request.
16371      */
16372     nocache : true,
16373
16374     /**
16375      * Load data from the configured URL, read the data object into
16376      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16377      * process that block using the passed callback.
16378      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16379      * for the request to the remote server.
16380      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16381      * object into a block of Roo.data.Records.
16382      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16383      * The function must be passed <ul>
16384      * <li>The Record block object</li>
16385      * <li>The "arg" argument from the load function</li>
16386      * <li>A boolean success indicator</li>
16387      * </ul>
16388      * @param {Object} scope The scope in which to call the callback
16389      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16390      */
16391     load : function(params, reader, callback, scope, arg){
16392         if(this.fireEvent("beforeload", this, params) !== false){
16393
16394             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16395
16396             var url = this.url;
16397             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16398             if(this.nocache){
16399                 url += "&_dc=" + (new Date().getTime());
16400             }
16401             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16402             var trans = {
16403                 id : transId,
16404                 cb : "stcCallback"+transId,
16405                 scriptId : "stcScript"+transId,
16406                 params : params,
16407                 arg : arg,
16408                 url : url,
16409                 callback : callback,
16410                 scope : scope,
16411                 reader : reader
16412             };
16413             var conn = this;
16414
16415             window[trans.cb] = function(o){
16416                 conn.handleResponse(o, trans);
16417             };
16418
16419             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16420
16421             if(this.autoAbort !== false){
16422                 this.abort();
16423             }
16424
16425             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16426
16427             var script = document.createElement("script");
16428             script.setAttribute("src", url);
16429             script.setAttribute("type", "text/javascript");
16430             script.setAttribute("id", trans.scriptId);
16431             this.head.appendChild(script);
16432
16433             this.trans = trans;
16434         }else{
16435             callback.call(scope||this, null, arg, false);
16436         }
16437     },
16438
16439     // private
16440     isLoading : function(){
16441         return this.trans ? true : false;
16442     },
16443
16444     /**
16445      * Abort the current server request.
16446      */
16447     abort : function(){
16448         if(this.isLoading()){
16449             this.destroyTrans(this.trans);
16450         }
16451     },
16452
16453     // private
16454     destroyTrans : function(trans, isLoaded){
16455         this.head.removeChild(document.getElementById(trans.scriptId));
16456         clearTimeout(trans.timeoutId);
16457         if(isLoaded){
16458             window[trans.cb] = undefined;
16459             try{
16460                 delete window[trans.cb];
16461             }catch(e){}
16462         }else{
16463             // if hasn't been loaded, wait for load to remove it to prevent script error
16464             window[trans.cb] = function(){
16465                 window[trans.cb] = undefined;
16466                 try{
16467                     delete window[trans.cb];
16468                 }catch(e){}
16469             };
16470         }
16471     },
16472
16473     // private
16474     handleResponse : function(o, trans){
16475         this.trans = false;
16476         this.destroyTrans(trans, true);
16477         var result;
16478         try {
16479             result = trans.reader.readRecords(o);
16480         }catch(e){
16481             this.fireEvent("loadexception", this, o, trans.arg, e);
16482             trans.callback.call(trans.scope||window, null, trans.arg, false);
16483             return;
16484         }
16485         this.fireEvent("load", this, o, trans.arg);
16486         trans.callback.call(trans.scope||window, result, trans.arg, true);
16487     },
16488
16489     // private
16490     handleFailure : function(trans){
16491         this.trans = false;
16492         this.destroyTrans(trans, false);
16493         this.fireEvent("loadexception", this, null, trans.arg);
16494         trans.callback.call(trans.scope||window, null, trans.arg, false);
16495     }
16496 });/*
16497  * Based on:
16498  * Ext JS Library 1.1.1
16499  * Copyright(c) 2006-2007, Ext JS, LLC.
16500  *
16501  * Originally Released Under LGPL - original licence link has changed is not relivant.
16502  *
16503  * Fork - LGPL
16504  * <script type="text/javascript">
16505  */
16506
16507 /**
16508  * @class Roo.data.JsonReader
16509  * @extends Roo.data.DataReader
16510  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16511  * based on mappings in a provided Roo.data.Record constructor.
16512  * 
16513  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16514  * in the reply previously. 
16515  * 
16516  * <p>
16517  * Example code:
16518  * <pre><code>
16519 var RecordDef = Roo.data.Record.create([
16520     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16521     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16522 ]);
16523 var myReader = new Roo.data.JsonReader({
16524     totalProperty: "results",    // The property which contains the total dataset size (optional)
16525     root: "rows",                // The property which contains an Array of row objects
16526     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16527 }, RecordDef);
16528 </code></pre>
16529  * <p>
16530  * This would consume a JSON file like this:
16531  * <pre><code>
16532 { 'results': 2, 'rows': [
16533     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16534     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16535 }
16536 </code></pre>
16537  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16538  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16539  * paged from the remote server.
16540  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16541  * @cfg {String} root name of the property which contains the Array of row objects.
16542  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16543  * @cfg {Array} fields Array of field definition objects
16544  * @constructor
16545  * Create a new JsonReader
16546  * @param {Object} meta Metadata configuration options
16547  * @param {Object} recordType Either an Array of field definition objects,
16548  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16549  */
16550 Roo.data.JsonReader = function(meta, recordType){
16551     
16552     meta = meta || {};
16553     // set some defaults:
16554     Roo.applyIf(meta, {
16555         totalProperty: 'total',
16556         successProperty : 'success',
16557         root : 'data',
16558         id : 'id'
16559     });
16560     
16561     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16562 };
16563 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16564     
16565     readerType : 'Json',
16566     
16567     /**
16568      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16569      * Used by Store query builder to append _requestMeta to params.
16570      * 
16571      */
16572     metaFromRemote : false,
16573     /**
16574      * This method is only used by a DataProxy which has retrieved data from a remote server.
16575      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16576      * @return {Object} data A data block which is used by an Roo.data.Store object as
16577      * a cache of Roo.data.Records.
16578      */
16579     read : function(response){
16580         var json = response.responseText;
16581        
16582         var o = /* eval:var:o */ eval("("+json+")");
16583         if(!o) {
16584             throw {message: "JsonReader.read: Json object not found"};
16585         }
16586         
16587         if(o.metaData){
16588             
16589             delete this.ef;
16590             this.metaFromRemote = true;
16591             this.meta = o.metaData;
16592             this.recordType = Roo.data.Record.create(o.metaData.fields);
16593             this.onMetaChange(this.meta, this.recordType, o);
16594         }
16595         return this.readRecords(o);
16596     },
16597
16598     // private function a store will implement
16599     onMetaChange : function(meta, recordType, o){
16600
16601     },
16602
16603     /**
16604          * @ignore
16605          */
16606     simpleAccess: function(obj, subsc) {
16607         return obj[subsc];
16608     },
16609
16610         /**
16611          * @ignore
16612          */
16613     getJsonAccessor: function(){
16614         var re = /[\[\.]/;
16615         return function(expr) {
16616             try {
16617                 return(re.test(expr))
16618                     ? new Function("obj", "return obj." + expr)
16619                     : function(obj){
16620                         return obj[expr];
16621                     };
16622             } catch(e){}
16623             return Roo.emptyFn;
16624         };
16625     }(),
16626
16627     /**
16628      * Create a data block containing Roo.data.Records from an XML document.
16629      * @param {Object} o An object which contains an Array of row objects in the property specified
16630      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16631      * which contains the total size of the dataset.
16632      * @return {Object} data A data block which is used by an Roo.data.Store object as
16633      * a cache of Roo.data.Records.
16634      */
16635     readRecords : function(o){
16636         /**
16637          * After any data loads, the raw JSON data is available for further custom processing.
16638          * @type Object
16639          */
16640         this.o = o;
16641         var s = this.meta, Record = this.recordType,
16642             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16643
16644 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16645         if (!this.ef) {
16646             if(s.totalProperty) {
16647                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16648                 }
16649                 if(s.successProperty) {
16650                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16651                 }
16652                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16653                 if (s.id) {
16654                         var g = this.getJsonAccessor(s.id);
16655                         this.getId = function(rec) {
16656                                 var r = g(rec);  
16657                                 return (r === undefined || r === "") ? null : r;
16658                         };
16659                 } else {
16660                         this.getId = function(){return null;};
16661                 }
16662             this.ef = [];
16663             for(var jj = 0; jj < fl; jj++){
16664                 f = fi[jj];
16665                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16666                 this.ef[jj] = this.getJsonAccessor(map);
16667             }
16668         }
16669
16670         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16671         if(s.totalProperty){
16672             var vt = parseInt(this.getTotal(o), 10);
16673             if(!isNaN(vt)){
16674                 totalRecords = vt;
16675             }
16676         }
16677         if(s.successProperty){
16678             var vs = this.getSuccess(o);
16679             if(vs === false || vs === 'false'){
16680                 success = false;
16681             }
16682         }
16683         var records = [];
16684         for(var i = 0; i < c; i++){
16685             var n = root[i];
16686             var values = {};
16687             var id = this.getId(n);
16688             for(var j = 0; j < fl; j++){
16689                 f = fi[j];
16690                                 var v = this.ef[j](n);
16691                                 if (!f.convert) {
16692                                         Roo.log('missing convert for ' + f.name);
16693                                         Roo.log(f);
16694                                         continue;
16695                                 }
16696                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16697             }
16698                         if (!Record) {
16699                                 return {
16700                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16701                                         success : false,
16702                                         records : [],
16703                                         totalRecords : 0
16704                                 };
16705                         }
16706             var record = new Record(values, id);
16707             record.json = n;
16708             records[i] = record;
16709         }
16710         return {
16711             raw : o,
16712             success : success,
16713             records : records,
16714             totalRecords : totalRecords
16715         };
16716     },
16717     // used when loading children.. @see loadDataFromChildren
16718     toLoadData: function(rec)
16719     {
16720         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16721         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16722         return { data : data, total : data.length };
16723         
16724     }
16725 });/*
16726  * Based on:
16727  * Ext JS Library 1.1.1
16728  * Copyright(c) 2006-2007, Ext JS, LLC.
16729  *
16730  * Originally Released Under LGPL - original licence link has changed is not relivant.
16731  *
16732  * Fork - LGPL
16733  * <script type="text/javascript">
16734  */
16735
16736 /**
16737  * @class Roo.data.ArrayReader
16738  * @extends Roo.data.DataReader
16739  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16740  * Each element of that Array represents a row of data fields. The
16741  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16742  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16743  * <p>
16744  * Example code:.
16745  * <pre><code>
16746 var RecordDef = Roo.data.Record.create([
16747     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16748     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16749 ]);
16750 var myReader = new Roo.data.ArrayReader({
16751     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16752 }, RecordDef);
16753 </code></pre>
16754  * <p>
16755  * This would consume an Array like this:
16756  * <pre><code>
16757 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16758   </code></pre>
16759  
16760  * @constructor
16761  * Create a new JsonReader
16762  * @param {Object} meta Metadata configuration options.
16763  * @param {Object|Array} recordType Either an Array of field definition objects
16764  * 
16765  * @cfg {Array} fields Array of field definition objects
16766  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16767  * as specified to {@link Roo.data.Record#create},
16768  * or an {@link Roo.data.Record} object
16769  *
16770  * 
16771  * created using {@link Roo.data.Record#create}.
16772  */
16773 Roo.data.ArrayReader = function(meta, recordType)
16774 {    
16775     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16776 };
16777
16778 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16779     
16780       /**
16781      * Create a data block containing Roo.data.Records from an XML document.
16782      * @param {Object} o An Array of row objects which represents the dataset.
16783      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16784      * a cache of Roo.data.Records.
16785      */
16786     readRecords : function(o)
16787     {
16788         var sid = this.meta ? this.meta.id : null;
16789         var recordType = this.recordType, fields = recordType.prototype.fields;
16790         var records = [];
16791         var root = o;
16792         for(var i = 0; i < root.length; i++){
16793             var n = root[i];
16794             var values = {};
16795             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16796             for(var j = 0, jlen = fields.length; j < jlen; j++){
16797                 var f = fields.items[j];
16798                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16799                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16800                 v = f.convert(v);
16801                 values[f.name] = v;
16802             }
16803             var record = new recordType(values, id);
16804             record.json = n;
16805             records[records.length] = record;
16806         }
16807         return {
16808             records : records,
16809             totalRecords : records.length
16810         };
16811     },
16812     // used when loading children.. @see loadDataFromChildren
16813     toLoadData: function(rec)
16814     {
16815         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16816         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16817         
16818     }
16819     
16820     
16821 });/*
16822  * - LGPL
16823  * * 
16824  */
16825
16826 /**
16827  * @class Roo.bootstrap.form.ComboBox
16828  * @extends Roo.bootstrap.form.TriggerField
16829  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16830  * @cfg {Boolean} append (true|false) default false
16831  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16832  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16833  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16834  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16835  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16836  * @cfg {Boolean} animate default true
16837  * @cfg {Boolean} emptyResultText only for touch device
16838  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16839  * @cfg {String} emptyTitle default ''
16840  * @cfg {Number} width fixed with? experimental
16841  * @constructor
16842  * Create a new ComboBox.
16843  * @param {Object} config Configuration options
16844  */
16845 Roo.bootstrap.form.ComboBox = function(config){
16846     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16847     this.addEvents({
16848         /**
16849          * @event expand
16850          * Fires when the dropdown list is expanded
16851         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16852         */
16853         'expand' : true,
16854         /**
16855          * @event collapse
16856          * Fires when the dropdown list is collapsed
16857         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16858         */
16859         'collapse' : true,
16860         /**
16861          * @event beforeselect
16862          * Fires before a list item is selected. Return false to cancel the selection.
16863         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16864         * @param {Roo.data.Record} record The data record returned from the underlying store
16865         * @param {Number} index The index of the selected item in the dropdown list
16866         */
16867         'beforeselect' : true,
16868         /**
16869          * @event select
16870          * Fires when a list item is selected
16871         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16872         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16873         * @param {Number} index The index of the selected item in the dropdown list
16874         */
16875         'select' : true,
16876         /**
16877          * @event beforequery
16878          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16879          * The event object passed has these properties:
16880         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16881         * @param {String} query The query
16882         * @param {Boolean} forceAll true to force "all" query
16883         * @param {Boolean} cancel true to cancel the query
16884         * @param {Object} e The query event object
16885         */
16886         'beforequery': true,
16887          /**
16888          * @event add
16889          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16890         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16891         */
16892         'add' : true,
16893         /**
16894          * @event edit
16895          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16896         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16897         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16898         */
16899         'edit' : true,
16900         /**
16901          * @event remove
16902          * Fires when the remove value from the combobox array
16903         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16904         */
16905         'remove' : true,
16906         /**
16907          * @event afterremove
16908          * Fires when the remove value from the combobox array
16909         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16910         */
16911         'afterremove' : true,
16912         /**
16913          * @event specialfilter
16914          * Fires when specialfilter
16915             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16916             */
16917         'specialfilter' : true,
16918         /**
16919          * @event tick
16920          * Fires when tick the element
16921             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16922             */
16923         'tick' : true,
16924         /**
16925          * @event touchviewdisplay
16926          * Fires when touch view require special display (default is using displayField)
16927             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16928             * @param {Object} cfg set html .
16929             */
16930         'touchviewdisplay' : true
16931         
16932     });
16933     
16934     this.item = [];
16935     this.tickItems = [];
16936     
16937     this.selectedIndex = -1;
16938     if(this.mode == 'local'){
16939         if(config.queryDelay === undefined){
16940             this.queryDelay = 10;
16941         }
16942         if(config.minChars === undefined){
16943             this.minChars = 0;
16944         }
16945     }
16946 };
16947
16948 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16949      
16950     /**
16951      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16952      * rendering into an Roo.Editor, defaults to false)
16953      */
16954     /**
16955      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16956      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16957      */
16958     /**
16959      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16960      */
16961     /**
16962      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16963      * the dropdown list (defaults to undefined, with no header element)
16964      */
16965
16966      /**
16967      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16968      */
16969      
16970      /**
16971      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16972      */
16973     listWidth: undefined,
16974     /**
16975      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16976      * mode = 'remote' or 'text' if mode = 'local')
16977      */
16978     displayField: undefined,
16979     
16980     /**
16981      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16982      * mode = 'remote' or 'value' if mode = 'local'). 
16983      * Note: use of a valueField requires the user make a selection
16984      * in order for a value to be mapped.
16985      */
16986     valueField: undefined,
16987     /**
16988      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16989      */
16990     modalTitle : '',
16991     
16992     /**
16993      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16994      * field's data value (defaults to the underlying DOM element's name)
16995      */
16996     hiddenName: undefined,
16997     /**
16998      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16999      */
17000     listClass: '',
17001     /**
17002      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17003      */
17004     selectedClass: 'active',
17005     
17006     /**
17007      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17008      */
17009     shadow:'sides',
17010     /**
17011      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17012      * anchor positions (defaults to 'tl-bl')
17013      */
17014     listAlign: 'tl-bl?',
17015     /**
17016      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17017      */
17018     maxHeight: 300,
17019     /**
17020      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17021      * query specified by the allQuery config option (defaults to 'query')
17022      */
17023     triggerAction: 'query',
17024     /**
17025      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17026      * (defaults to 4, does not apply if editable = false)
17027      */
17028     minChars : 4,
17029     /**
17030      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17031      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17032      */
17033     typeAhead: false,
17034     /**
17035      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17036      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17037      */
17038     queryDelay: 500,
17039     /**
17040      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17041      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17042      */
17043     pageSize: 0,
17044     /**
17045      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17046      * when editable = true (defaults to false)
17047      */
17048     selectOnFocus:false,
17049     /**
17050      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17051      */
17052     queryParam: 'query',
17053     /**
17054      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17055      * when mode = 'remote' (defaults to 'Loading...')
17056      */
17057     loadingText: 'Loading...',
17058     /**
17059      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17060      */
17061     resizable: false,
17062     /**
17063      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17064      */
17065     handleHeight : 8,
17066     /**
17067      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17068      * traditional select (defaults to true)
17069      */
17070     editable: true,
17071     /**
17072      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17073      */
17074     allQuery: '',
17075     /**
17076      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17077      */
17078     mode: 'remote',
17079     /**
17080      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17081      * listWidth has a higher value)
17082      */
17083     minListWidth : 70,
17084     /**
17085      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17086      * allow the user to set arbitrary text into the field (defaults to false)
17087      */
17088     forceSelection:false,
17089     /**
17090      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17091      * if typeAhead = true (defaults to 250)
17092      */
17093     typeAheadDelay : 250,
17094     /**
17095      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17096      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17097      */
17098     valueNotFoundText : undefined,
17099     /**
17100      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17101      */
17102     blockFocus : false,
17103     
17104     /**
17105      * @cfg {Boolean} disableClear Disable showing of clear button.
17106      */
17107     disableClear : false,
17108     /**
17109      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17110      */
17111     alwaysQuery : false,
17112     
17113     /**
17114      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17115      */
17116     multiple : false,
17117     
17118     /**
17119      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17120      */
17121     invalidClass : "has-warning",
17122     
17123     /**
17124      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17125      */
17126     validClass : "has-success",
17127     
17128     /**
17129      * @cfg {Boolean} specialFilter (true|false) special filter default false
17130      */
17131     specialFilter : false,
17132     
17133     /**
17134      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17135      */
17136     mobileTouchView : true,
17137     
17138     /**
17139      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17140      */
17141     useNativeIOS : false,
17142     
17143     /**
17144      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17145      */
17146     mobile_restrict_height : false,
17147     
17148     ios_options : false,
17149     
17150     //private
17151     addicon : false,
17152     editicon: false,
17153     
17154     page: 0,
17155     hasQuery: false,
17156     append: false,
17157     loadNext: false,
17158     autoFocus : true,
17159     tickable : false,
17160     btnPosition : 'right',
17161     triggerList : true,
17162     showToggleBtn : true,
17163     animate : true,
17164     emptyResultText: 'Empty',
17165     triggerText : 'Select',
17166     emptyTitle : '',
17167     width : false,
17168     
17169     // element that contains real text value.. (when hidden is used..)
17170     
17171     getAutoCreate : function()
17172     {   
17173         var cfg = false;
17174         //render
17175         /*
17176          * Render classic select for iso
17177          */
17178         
17179         if(Roo.isIOS && this.useNativeIOS){
17180             cfg = this.getAutoCreateNativeIOS();
17181             return cfg;
17182         }
17183         
17184         /*
17185          * Touch Devices
17186          */
17187         
17188         if(Roo.isTouch && this.mobileTouchView){
17189             cfg = this.getAutoCreateTouchView();
17190             return cfg;;
17191         }
17192         
17193         /*
17194          *  Normal ComboBox
17195          */
17196         if(!this.tickable){
17197             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17198             return cfg;
17199         }
17200         
17201         /*
17202          *  ComboBox with tickable selections
17203          */
17204              
17205         var align = this.labelAlign || this.parentLabelAlign();
17206         
17207         cfg = {
17208             cls : 'form-group roo-combobox-tickable' //input-group
17209         };
17210         
17211         var btn_text_select = '';
17212         var btn_text_done = '';
17213         var btn_text_cancel = '';
17214         
17215         if (this.btn_text_show) {
17216             btn_text_select = 'Select';
17217             btn_text_done = 'Done';
17218             btn_text_cancel = 'Cancel'; 
17219         }
17220         
17221         var buttons = {
17222             tag : 'div',
17223             cls : 'tickable-buttons',
17224             cn : [
17225                 {
17226                     tag : 'button',
17227                     type : 'button',
17228                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17229                     //html : this.triggerText
17230                     html: btn_text_select
17231                 },
17232                 {
17233                     tag : 'button',
17234                     type : 'button',
17235                     name : 'ok',
17236                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17237                     //html : 'Done'
17238                     html: btn_text_done
17239                 },
17240                 {
17241                     tag : 'button',
17242                     type : 'button',
17243                     name : 'cancel',
17244                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17245                     //html : 'Cancel'
17246                     html: btn_text_cancel
17247                 }
17248             ]
17249         };
17250         
17251         if(this.editable){
17252             buttons.cn.unshift({
17253                 tag: 'input',
17254                 cls: 'roo-select2-search-field-input'
17255             });
17256         }
17257         
17258         var _this = this;
17259         
17260         Roo.each(buttons.cn, function(c){
17261             if (_this.size) {
17262                 c.cls += ' btn-' + _this.size;
17263             }
17264
17265             if (_this.disabled) {
17266                 c.disabled = true;
17267             }
17268         });
17269         
17270         var box = {
17271             tag: 'div',
17272             style : 'display: contents',
17273             cn: [
17274                 {
17275                     tag: 'input',
17276                     type : 'hidden',
17277                     cls: 'form-hidden-field'
17278                 },
17279                 {
17280                     tag: 'ul',
17281                     cls: 'roo-select2-choices',
17282                     cn:[
17283                         {
17284                             tag: 'li',
17285                             cls: 'roo-select2-search-field',
17286                             cn: [
17287                                 buttons
17288                             ]
17289                         }
17290                     ]
17291                 }
17292             ]
17293         };
17294         
17295         var combobox = {
17296             cls: 'roo-select2-container input-group roo-select2-container-multi',
17297             cn: [
17298                 
17299                 box
17300 //                {
17301 //                    tag: 'ul',
17302 //                    cls: 'typeahead typeahead-long dropdown-menu',
17303 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17304 //                }
17305             ]
17306         };
17307         
17308         if(this.hasFeedback && !this.allowBlank){
17309             
17310             var feedback = {
17311                 tag: 'span',
17312                 cls: 'glyphicon form-control-feedback'
17313             };
17314
17315             combobox.cn.push(feedback);
17316         }
17317         
17318         
17319         
17320         var indicator = {
17321             tag : 'i',
17322             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17323             tooltip : 'This field is required'
17324         };
17325         if (Roo.bootstrap.version == 4) {
17326             indicator = {
17327                 tag : 'i',
17328                 style : 'display:none'
17329             };
17330         }
17331         if (align ==='left' && this.fieldLabel.length) {
17332             
17333             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17334             
17335             cfg.cn = [
17336                 indicator,
17337                 {
17338                     tag: 'label',
17339                     'for' :  id,
17340                     cls : 'control-label col-form-label',
17341                     html : this.fieldLabel
17342
17343                 },
17344                 {
17345                     cls : "", 
17346                     cn: [
17347                         combobox
17348                     ]
17349                 }
17350
17351             ];
17352             
17353             var labelCfg = cfg.cn[1];
17354             var contentCfg = cfg.cn[2];
17355             
17356
17357             if(this.indicatorpos == 'right'){
17358                 
17359                 cfg.cn = [
17360                     {
17361                         tag: 'label',
17362                         'for' :  id,
17363                         cls : 'control-label col-form-label',
17364                         cn : [
17365                             {
17366                                 tag : 'span',
17367                                 html : this.fieldLabel
17368                             },
17369                             indicator
17370                         ]
17371                     },
17372                     {
17373                         cls : "",
17374                         cn: [
17375                             combobox
17376                         ]
17377                     }
17378
17379                 ];
17380                 
17381                 
17382                 
17383                 labelCfg = cfg.cn[0];
17384                 contentCfg = cfg.cn[1];
17385             
17386             }
17387             
17388             if(this.labelWidth > 12){
17389                 labelCfg.style = "width: " + this.labelWidth + 'px';
17390             }
17391             if(this.width * 1 > 0){
17392                 contentCfg.style = "width: " + this.width + 'px';
17393             }
17394             if(this.labelWidth < 13 && this.labelmd == 0){
17395                 this.labelmd = this.labelWidth;
17396             }
17397             
17398             if(this.labellg > 0){
17399                 labelCfg.cls += ' col-lg-' + this.labellg;
17400                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17401             }
17402             
17403             if(this.labelmd > 0){
17404                 labelCfg.cls += ' col-md-' + this.labelmd;
17405                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17406             }
17407             
17408             if(this.labelsm > 0){
17409                 labelCfg.cls += ' col-sm-' + this.labelsm;
17410                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17411             }
17412             
17413             if(this.labelxs > 0){
17414                 labelCfg.cls += ' col-xs-' + this.labelxs;
17415                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17416             }
17417                 
17418                 
17419         } else if ( this.fieldLabel.length) {
17420 //                Roo.log(" label");
17421                  cfg.cn = [
17422                    indicator,
17423                     {
17424                         tag: 'label',
17425                         //cls : 'input-group-addon',
17426                         html : this.fieldLabel
17427                     },
17428                     combobox
17429                 ];
17430                 
17431                 if(this.indicatorpos == 'right'){
17432                     cfg.cn = [
17433                         {
17434                             tag: 'label',
17435                             //cls : 'input-group-addon',
17436                             html : this.fieldLabel
17437                         },
17438                         indicator,
17439                         combobox
17440                     ];
17441                     
17442                 }
17443
17444         } else {
17445             
17446 //                Roo.log(" no label && no align");
17447                 cfg = combobox
17448                      
17449                 
17450         }
17451          
17452         var settings=this;
17453         ['xs','sm','md','lg'].map(function(size){
17454             if (settings[size]) {
17455                 cfg.cls += ' col-' + size + '-' + settings[size];
17456             }
17457         });
17458         
17459         return cfg;
17460         
17461     },
17462     
17463     _initEventsCalled : false,
17464     
17465     // private
17466     initEvents: function()
17467     {   
17468         if (this._initEventsCalled) { // as we call render... prevent looping...
17469             return;
17470         }
17471         this._initEventsCalled = true;
17472         
17473         if (!this.store) {
17474             throw "can not find store for combo";
17475         }
17476         
17477         this.indicator = this.indicatorEl();
17478         
17479         this.store = Roo.factory(this.store, Roo.data);
17480         this.store.parent = this;
17481         
17482         // if we are building from html. then this element is so complex, that we can not really
17483         // use the rendered HTML.
17484         // so we have to trash and replace the previous code.
17485         if (Roo.XComponent.build_from_html) {
17486             // remove this element....
17487             var e = this.el.dom, k=0;
17488             while (e ) { e = e.previousSibling;  ++k;}
17489
17490             this.el.remove();
17491             
17492             this.el=false;
17493             this.rendered = false;
17494             
17495             this.render(this.parent().getChildContainer(true), k);
17496         }
17497         
17498         if(Roo.isIOS && this.useNativeIOS){
17499             this.initIOSView();
17500             return;
17501         }
17502         
17503         /*
17504          * Touch Devices
17505          */
17506         
17507         if(Roo.isTouch && this.mobileTouchView){
17508             this.initTouchView();
17509             return;
17510         }
17511         
17512         if(this.tickable){
17513             this.initTickableEvents();
17514             return;
17515         }
17516         
17517         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17518         
17519         if(this.hiddenName){
17520             
17521             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17522             
17523             this.hiddenField.dom.value =
17524                 this.hiddenValue !== undefined ? this.hiddenValue :
17525                 this.value !== undefined ? this.value : '';
17526
17527             // prevent input submission
17528             this.el.dom.removeAttribute('name');
17529             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17530              
17531              
17532         }
17533         //if(Roo.isGecko){
17534         //    this.el.dom.setAttribute('autocomplete', 'off');
17535         //}
17536         
17537         var cls = 'x-combo-list';
17538         
17539         //this.list = new Roo.Layer({
17540         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17541         //});
17542         
17543         var _this = this;
17544         
17545         (function(){
17546             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17547             _this.list.setWidth(lw);
17548         }).defer(100);
17549         
17550         this.list.on('mouseover', this.onViewOver, this);
17551         this.list.on('mousemove', this.onViewMove, this);
17552         this.list.on('scroll', this.onViewScroll, this);
17553         
17554         /*
17555         this.list.swallowEvent('mousewheel');
17556         this.assetHeight = 0;
17557
17558         if(this.title){
17559             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17560             this.assetHeight += this.header.getHeight();
17561         }
17562
17563         this.innerList = this.list.createChild({cls:cls+'-inner'});
17564         this.innerList.on('mouseover', this.onViewOver, this);
17565         this.innerList.on('mousemove', this.onViewMove, this);
17566         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17567         
17568         if(this.allowBlank && !this.pageSize && !this.disableClear){
17569             this.footer = this.list.createChild({cls:cls+'-ft'});
17570             this.pageTb = new Roo.Toolbar(this.footer);
17571            
17572         }
17573         if(this.pageSize){
17574             this.footer = this.list.createChild({cls:cls+'-ft'});
17575             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17576                     {pageSize: this.pageSize});
17577             
17578         }
17579         
17580         if (this.pageTb && this.allowBlank && !this.disableClear) {
17581             var _this = this;
17582             this.pageTb.add(new Roo.Toolbar.Fill(), {
17583                 cls: 'x-btn-icon x-btn-clear',
17584                 text: '&#160;',
17585                 handler: function()
17586                 {
17587                     _this.collapse();
17588                     _this.clearValue();
17589                     _this.onSelect(false, -1);
17590                 }
17591             });
17592         }
17593         if (this.footer) {
17594             this.assetHeight += this.footer.getHeight();
17595         }
17596         */
17597             
17598         if(!this.tpl){
17599             this.tpl = Roo.bootstrap.version == 4 ?
17600                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17601                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17602         }
17603
17604         this.view = new Roo.View(this.list, this.tpl, {
17605             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17606         });
17607         //this.view.wrapEl.setDisplayed(false);
17608         this.view.on('click', this.onViewClick, this);
17609         
17610         
17611         this.store.on('beforeload', this.onBeforeLoad, this);
17612         this.store.on('load', this.onLoad, this);
17613         this.store.on('loadexception', this.onLoadException, this);
17614         /*
17615         if(this.resizable){
17616             this.resizer = new Roo.Resizable(this.list,  {
17617                pinned:true, handles:'se'
17618             });
17619             this.resizer.on('resize', function(r, w, h){
17620                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17621                 this.listWidth = w;
17622                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17623                 this.restrictHeight();
17624             }, this);
17625             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17626         }
17627         */
17628         if(!this.editable){
17629             this.editable = true;
17630             this.setEditable(false);
17631         }
17632         
17633         /*
17634         
17635         if (typeof(this.events.add.listeners) != 'undefined') {
17636             
17637             this.addicon = this.wrap.createChild(
17638                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17639        
17640             this.addicon.on('click', function(e) {
17641                 this.fireEvent('add', this);
17642             }, this);
17643         }
17644         if (typeof(this.events.edit.listeners) != 'undefined') {
17645             
17646             this.editicon = this.wrap.createChild(
17647                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17648             if (this.addicon) {
17649                 this.editicon.setStyle('margin-left', '40px');
17650             }
17651             this.editicon.on('click', function(e) {
17652                 
17653                 // we fire even  if inothing is selected..
17654                 this.fireEvent('edit', this, this.lastData );
17655                 
17656             }, this);
17657         }
17658         */
17659         
17660         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17661             "up" : function(e){
17662                 this.inKeyMode = true;
17663                 this.selectPrev();
17664             },
17665
17666             "down" : function(e){
17667                 if(!this.isExpanded()){
17668                     this.onTriggerClick();
17669                 }else{
17670                     this.inKeyMode = true;
17671                     this.selectNext();
17672                 }
17673             },
17674
17675             "enter" : function(e){
17676 //                this.onViewClick();
17677                 //return true;
17678                 this.collapse();
17679                 
17680                 if(this.fireEvent("specialkey", this, e)){
17681                     this.onViewClick(false);
17682                 }
17683                 
17684                 return true;
17685             },
17686
17687             "esc" : function(e){
17688                 this.collapse();
17689             },
17690
17691             "tab" : function(e){
17692                 this.collapse();
17693                 
17694                 if(this.fireEvent("specialkey", this, e)){
17695                     this.onViewClick(false);
17696                 }
17697                 
17698                 return true;
17699             },
17700
17701             scope : this,
17702
17703             doRelay : function(foo, bar, hname){
17704                 if(hname == 'down' || this.scope.isExpanded()){
17705                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17706                 }
17707                 return true;
17708             },
17709
17710             forceKeyDown: true
17711         });
17712         
17713         
17714         this.queryDelay = Math.max(this.queryDelay || 10,
17715                 this.mode == 'local' ? 10 : 250);
17716         
17717         
17718         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17719         
17720         if(this.typeAhead){
17721             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17722         }
17723         if(this.editable !== false){
17724             this.inputEl().on("keyup", this.onKeyUp, this);
17725         }
17726         if(this.forceSelection){
17727             this.inputEl().on('blur', this.doForce, this);
17728         }
17729         
17730         if(this.multiple){
17731             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17732             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17733         }
17734     },
17735     
17736     initTickableEvents: function()
17737     {   
17738         this.createList();
17739         
17740         if(this.hiddenName){
17741             
17742             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17743             
17744             this.hiddenField.dom.value =
17745                 this.hiddenValue !== undefined ? this.hiddenValue :
17746                 this.value !== undefined ? this.value : '';
17747
17748             // prevent input submission
17749             this.el.dom.removeAttribute('name');
17750             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17751              
17752              
17753         }
17754         
17755 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17756         
17757         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17758         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17759         if(this.triggerList){
17760             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17761         }
17762          
17763         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17764         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17765         
17766         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17767         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17768         
17769         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17770         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17771         
17772         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17773         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17774         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17775         
17776         this.okBtn.hide();
17777         this.cancelBtn.hide();
17778         
17779         var _this = this;
17780         
17781         (function(){
17782             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17783             _this.list.setWidth(lw);
17784         }).defer(100);
17785         
17786         this.list.on('mouseover', this.onViewOver, this);
17787         this.list.on('mousemove', this.onViewMove, this);
17788         
17789         this.list.on('scroll', this.onViewScroll, this);
17790         
17791         if(!this.tpl){
17792             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17793                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17794         }
17795
17796         this.view = new Roo.View(this.list, this.tpl, {
17797             singleSelect:true,
17798             tickable:true,
17799             parent:this,
17800             store: this.store,
17801             selectedClass: this.selectedClass
17802         });
17803         
17804         //this.view.wrapEl.setDisplayed(false);
17805         this.view.on('click', this.onViewClick, this);
17806         
17807         
17808         
17809         this.store.on('beforeload', this.onBeforeLoad, this);
17810         this.store.on('load', this.onLoad, this);
17811         this.store.on('loadexception', this.onLoadException, this);
17812         
17813         if(this.editable){
17814             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17815                 "up" : function(e){
17816                     this.inKeyMode = true;
17817                     this.selectPrev();
17818                 },
17819
17820                 "down" : function(e){
17821                     this.inKeyMode = true;
17822                     this.selectNext();
17823                 },
17824
17825                 "enter" : function(e){
17826                     if(this.fireEvent("specialkey", this, e)){
17827                         this.onViewClick(false);
17828                     }
17829                     
17830                     return true;
17831                 },
17832
17833                 "esc" : function(e){
17834                     this.onTickableFooterButtonClick(e, false, false);
17835                 },
17836
17837                 "tab" : function(e){
17838                     this.fireEvent("specialkey", this, e);
17839                     
17840                     this.onTickableFooterButtonClick(e, false, false);
17841                     
17842                     return true;
17843                 },
17844
17845                 scope : this,
17846
17847                 doRelay : function(e, fn, key){
17848                     if(this.scope.isExpanded()){
17849                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17850                     }
17851                     return true;
17852                 },
17853
17854                 forceKeyDown: true
17855             });
17856         }
17857         
17858         this.queryDelay = Math.max(this.queryDelay || 10,
17859                 this.mode == 'local' ? 10 : 250);
17860         
17861         
17862         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17863         
17864         if(this.typeAhead){
17865             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17866         }
17867         
17868         if(this.editable !== false){
17869             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17870         }
17871         
17872         this.indicator = this.indicatorEl();
17873         
17874         if(this.indicator){
17875             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17876             this.indicator.hide();
17877         }
17878         
17879     },
17880
17881     onDestroy : function(){
17882         if(this.view){
17883             this.view.setStore(null);
17884             this.view.el.removeAllListeners();
17885             this.view.el.remove();
17886             this.view.purgeListeners();
17887         }
17888         if(this.list){
17889             this.list.dom.innerHTML  = '';
17890         }
17891         
17892         if(this.store){
17893             this.store.un('beforeload', this.onBeforeLoad, this);
17894             this.store.un('load', this.onLoad, this);
17895             this.store.un('loadexception', this.onLoadException, this);
17896         }
17897         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17898     },
17899
17900     // private
17901     fireKey : function(e){
17902         if(e.isNavKeyPress() && !this.list.isVisible()){
17903             this.fireEvent("specialkey", this, e);
17904         }
17905     },
17906
17907     // private
17908     onResize: function(w, h)
17909     {
17910         
17911         
17912 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17913 //        
17914 //        if(typeof w != 'number'){
17915 //            // we do not handle it!?!?
17916 //            return;
17917 //        }
17918 //        var tw = this.trigger.getWidth();
17919 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17920 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17921 //        var x = w - tw;
17922 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17923 //            
17924 //        //this.trigger.setStyle('left', x+'px');
17925 //        
17926 //        if(this.list && this.listWidth === undefined){
17927 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17928 //            this.list.setWidth(lw);
17929 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17930 //        }
17931         
17932     
17933         
17934     },
17935
17936     /**
17937      * Allow or prevent the user from directly editing the field text.  If false is passed,
17938      * the user will only be able to select from the items defined in the dropdown list.  This method
17939      * is the runtime equivalent of setting the 'editable' config option at config time.
17940      * @param {Boolean} value True to allow the user to directly edit the field text
17941      */
17942     setEditable : function(value){
17943         if(value == this.editable){
17944             return;
17945         }
17946         this.editable = value;
17947         if(!value){
17948             this.inputEl().dom.setAttribute('readOnly', true);
17949             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17950             this.inputEl().addClass('x-combo-noedit');
17951         }else{
17952             this.inputEl().dom.removeAttribute('readOnly');
17953             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17954             this.inputEl().removeClass('x-combo-noedit');
17955         }
17956     },
17957
17958     // private
17959     
17960     onBeforeLoad : function(combo,opts){
17961         if(!this.hasFocus){
17962             return;
17963         }
17964          if (!opts.add) {
17965             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17966          }
17967         this.restrictHeight();
17968         this.selectedIndex = -1;
17969     },
17970
17971     // private
17972     onLoad : function(){
17973         
17974         this.hasQuery = false;
17975         
17976         if(!this.hasFocus){
17977             return;
17978         }
17979         
17980         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17981             this.loading.hide();
17982         }
17983         
17984         if(this.store.getCount() > 0){
17985             
17986             this.expand();
17987             this.restrictHeight();
17988             if(this.lastQuery == this.allQuery){
17989                 if(this.editable && !this.tickable){
17990                     this.inputEl().dom.select();
17991                 }
17992                 
17993                 if(
17994                     !this.selectByValue(this.value, true) &&
17995                     this.autoFocus && 
17996                     (
17997                         !this.store.lastOptions ||
17998                         typeof(this.store.lastOptions.add) == 'undefined' || 
17999                         this.store.lastOptions.add != true
18000                     )
18001                 ){
18002                     this.select(0, true);
18003                 }
18004             }else{
18005                 if(this.autoFocus){
18006                     this.selectNext();
18007                 }
18008                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18009                     this.taTask.delay(this.typeAheadDelay);
18010                 }
18011             }
18012         }else{
18013             this.onEmptyResults();
18014         }
18015         
18016         //this.el.focus();
18017     },
18018     // private
18019     onLoadException : function()
18020     {
18021         this.hasQuery = false;
18022         
18023         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18024             this.loading.hide();
18025         }
18026         
18027         if(this.tickable && this.editable){
18028             return;
18029         }
18030         
18031         this.collapse();
18032         // only causes errors at present
18033         //Roo.log(this.store.reader.jsonData);
18034         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18035             // fixme
18036             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18037         //}
18038         
18039         
18040     },
18041     // private
18042     onTypeAhead : function(){
18043         if(this.store.getCount() > 0){
18044             var r = this.store.getAt(0);
18045             var newValue = r.data[this.displayField];
18046             var len = newValue.length;
18047             var selStart = this.getRawValue().length;
18048             
18049             if(selStart != len){
18050                 this.setRawValue(newValue);
18051                 this.selectText(selStart, newValue.length);
18052             }
18053         }
18054     },
18055
18056     // private
18057     onSelect : function(record, index){
18058         
18059         if(this.fireEvent('beforeselect', this, record, index) !== false){
18060         
18061             this.setFromData(index > -1 ? record.data : false);
18062             
18063             this.collapse();
18064             this.fireEvent('select', this, record, index);
18065         }
18066     },
18067
18068     /**
18069      * Returns the currently selected field value or empty string if no value is set.
18070      * @return {String} value The selected value
18071      */
18072     getValue : function()
18073     {
18074         if(Roo.isIOS && this.useNativeIOS){
18075             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18076         }
18077         
18078         if(this.multiple){
18079             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18080         }
18081         
18082         if(this.valueField){
18083             return typeof this.value != 'undefined' ? this.value : '';
18084         }else{
18085             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18086         }
18087     },
18088     
18089     getRawValue : function()
18090     {
18091         if(Roo.isIOS && this.useNativeIOS){
18092             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18093         }
18094         
18095         var v = this.inputEl().getValue();
18096         
18097         return v;
18098     },
18099
18100     /**
18101      * Clears any text/value currently set in the field
18102      */
18103     clearValue : function(){
18104         
18105         if(this.hiddenField){
18106             this.hiddenField.dom.value = '';
18107         }
18108         this.value = '';
18109         this.setRawValue('');
18110         this.lastSelectionText = '';
18111         this.lastData = false;
18112         
18113         var close = this.closeTriggerEl();
18114         
18115         if(close){
18116             close.hide();
18117         }
18118         
18119         this.validate();
18120         
18121     },
18122
18123     /**
18124      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18125      * will be displayed in the field.  If the value does not match the data value of an existing item,
18126      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18127      * Otherwise the field will be blank (although the value will still be set).
18128      * @param {String} value The value to match
18129      */
18130     setValue : function(v)
18131     {
18132         if(Roo.isIOS && this.useNativeIOS){
18133             this.setIOSValue(v);
18134             return;
18135         }
18136         
18137         if(this.multiple){
18138             this.syncValue();
18139             return;
18140         }
18141         
18142         var text = v;
18143         if(this.valueField){
18144             var r = this.findRecord(this.valueField, v);
18145             if(r){
18146                 text = r.data[this.displayField];
18147             }else if(this.valueNotFoundText !== undefined){
18148                 text = this.valueNotFoundText;
18149             }
18150         }
18151         this.lastSelectionText = text;
18152         if(this.hiddenField){
18153             this.hiddenField.dom.value = v;
18154         }
18155         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18156         this.value = v;
18157         
18158         var close = this.closeTriggerEl();
18159         
18160         if(close){
18161             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18162         }
18163         
18164         this.validate();
18165     },
18166     /**
18167      * @property {Object} the last set data for the element
18168      */
18169     
18170     lastData : false,
18171     /**
18172      * Sets the value of the field based on a object which is related to the record format for the store.
18173      * @param {Object} value the value to set as. or false on reset?
18174      */
18175     setFromData : function(o){
18176         
18177         if(this.multiple){
18178             this.addItem(o);
18179             return;
18180         }
18181             
18182         var dv = ''; // display value
18183         var vv = ''; // value value..
18184         this.lastData = o;
18185         if (this.displayField) {
18186             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18187         } else {
18188             // this is an error condition!!!
18189             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18190         }
18191         
18192         if(this.valueField){
18193             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18194         }
18195         
18196         var close = this.closeTriggerEl();
18197         
18198         if(close){
18199             if(dv.length || vv * 1 > 0){
18200                 close.show() ;
18201                 this.blockFocus=true;
18202             } else {
18203                 close.hide();
18204             }             
18205         }
18206         
18207         if(this.hiddenField){
18208             this.hiddenField.dom.value = vv;
18209             
18210             this.lastSelectionText = dv;
18211             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18212             this.value = vv;
18213             return;
18214         }
18215         // no hidden field.. - we store the value in 'value', but still display
18216         // display field!!!!
18217         this.lastSelectionText = dv;
18218         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18219         this.value = vv;
18220         
18221         
18222         
18223     },
18224     // private
18225     reset : function(){
18226         // overridden so that last data is reset..
18227         
18228         if(this.multiple){
18229             this.clearItem();
18230             return;
18231         }
18232         
18233         this.setValue(this.originalValue);
18234         //this.clearInvalid();
18235         this.lastData = false;
18236         if (this.view) {
18237             this.view.clearSelections();
18238         }
18239         
18240         this.validate();
18241     },
18242     // private
18243     findRecord : function(prop, value){
18244         var record;
18245         if(this.store.getCount() > 0){
18246             this.store.each(function(r){
18247                 if(r.data[prop] == value){
18248                     record = r;
18249                     return false;
18250                 }
18251                 return true;
18252             });
18253         }
18254         return record;
18255     },
18256     
18257     getName: function()
18258     {
18259         // returns hidden if it's set..
18260         if (!this.rendered) {return ''};
18261         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18262         
18263     },
18264     // private
18265     onViewMove : function(e, t){
18266         this.inKeyMode = false;
18267     },
18268
18269     // private
18270     onViewOver : function(e, t){
18271         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18272             return;
18273         }
18274         var item = this.view.findItemFromChild(t);
18275         
18276         if(item){
18277             var index = this.view.indexOf(item);
18278             this.select(index, false);
18279         }
18280     },
18281
18282     // private
18283     onViewClick : function(view, doFocus, el, e)
18284     {
18285         var index = this.view.getSelectedIndexes()[0];
18286         
18287         var r = this.store.getAt(index);
18288         
18289         if(this.tickable){
18290             
18291             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18292                 return;
18293             }
18294             
18295             var rm = false;
18296             var _this = this;
18297             
18298             Roo.each(this.tickItems, function(v,k){
18299                 
18300                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18301                     Roo.log(v);
18302                     _this.tickItems.splice(k, 1);
18303                     
18304                     if(typeof(e) == 'undefined' && view == false){
18305                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18306                     }
18307                     
18308                     rm = true;
18309                     return;
18310                 }
18311             });
18312             
18313             if(rm){
18314                 return;
18315             }
18316             
18317             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18318                 this.tickItems.push(r.data);
18319             }
18320             
18321             if(typeof(e) == 'undefined' && view == false){
18322                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18323             }
18324                     
18325             return;
18326         }
18327         
18328         if(r){
18329             this.onSelect(r, index);
18330         }
18331         if(doFocus !== false && !this.blockFocus){
18332             this.inputEl().focus();
18333         }
18334     },
18335
18336     // private
18337     restrictHeight : function(){
18338         //this.innerList.dom.style.height = '';
18339         //var inner = this.innerList.dom;
18340         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18341         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18342         //this.list.beginUpdate();
18343         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18344         this.list.alignTo(this.inputEl(), this.listAlign);
18345         this.list.alignTo(this.inputEl(), this.listAlign);
18346         //this.list.endUpdate();
18347     },
18348
18349     // private
18350     onEmptyResults : function(){
18351         
18352         if(this.tickable && this.editable){
18353             this.hasFocus = false;
18354             this.restrictHeight();
18355             return;
18356         }
18357         
18358         this.collapse();
18359     },
18360
18361     /**
18362      * Returns true if the dropdown list is expanded, else false.
18363      */
18364     isExpanded : function(){
18365         return this.list.isVisible();
18366     },
18367
18368     /**
18369      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18370      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18371      * @param {String} value The data value of the item to select
18372      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18373      * selected item if it is not currently in view (defaults to true)
18374      * @return {Boolean} True if the value matched an item in the list, else false
18375      */
18376     selectByValue : function(v, scrollIntoView){
18377         if(v !== undefined && v !== null){
18378             var r = this.findRecord(this.valueField || this.displayField, v);
18379             if(r){
18380                 this.select(this.store.indexOf(r), scrollIntoView);
18381                 return true;
18382             }
18383         }
18384         return false;
18385     },
18386
18387     /**
18388      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18389      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18390      * @param {Number} index The zero-based index of the list item to select
18391      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18392      * selected item if it is not currently in view (defaults to true)
18393      */
18394     select : function(index, scrollIntoView){
18395         this.selectedIndex = index;
18396         this.view.select(index);
18397         if(scrollIntoView !== false){
18398             var el = this.view.getNode(index);
18399             /*
18400              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18401              */
18402             if(el){
18403                 this.list.scrollChildIntoView(el, false);
18404             }
18405         }
18406     },
18407
18408     // private
18409     selectNext : function(){
18410         var ct = this.store.getCount();
18411         if(ct > 0){
18412             if(this.selectedIndex == -1){
18413                 this.select(0);
18414             }else if(this.selectedIndex < ct-1){
18415                 this.select(this.selectedIndex+1);
18416             }
18417         }
18418     },
18419
18420     // private
18421     selectPrev : function(){
18422         var ct = this.store.getCount();
18423         if(ct > 0){
18424             if(this.selectedIndex == -1){
18425                 this.select(0);
18426             }else if(this.selectedIndex != 0){
18427                 this.select(this.selectedIndex-1);
18428             }
18429         }
18430     },
18431
18432     // private
18433     onKeyUp : function(e){
18434         if(this.editable !== false && !e.isSpecialKey()){
18435             this.lastKey = e.getKey();
18436             this.dqTask.delay(this.queryDelay);
18437         }
18438     },
18439
18440     // private
18441     validateBlur : function(){
18442         return !this.list || !this.list.isVisible();   
18443     },
18444
18445     // private
18446     initQuery : function(){
18447         
18448         var v = this.getRawValue();
18449         
18450         if(this.tickable && this.editable){
18451             v = this.tickableInputEl().getValue();
18452         }
18453         
18454         this.doQuery(v);
18455     },
18456
18457     // private
18458     doForce : function(){
18459         if(this.inputEl().dom.value.length > 0){
18460             this.inputEl().dom.value =
18461                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18462              
18463         }
18464     },
18465
18466     /**
18467      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18468      * query allowing the query action to be canceled if needed.
18469      * @param {String} query The SQL query to execute
18470      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18471      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18472      * saved in the current store (defaults to false)
18473      */
18474     doQuery : function(q, forceAll){
18475         
18476         if(q === undefined || q === null){
18477             q = '';
18478         }
18479         var qe = {
18480             query: q,
18481             forceAll: forceAll,
18482             combo: this,
18483             cancel:false
18484         };
18485         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18486             return false;
18487         }
18488         q = qe.query;
18489         
18490         forceAll = qe.forceAll;
18491         if(forceAll === true || (q.length >= this.minChars)){
18492             
18493             this.hasQuery = true;
18494             
18495             if(this.lastQuery != q || this.alwaysQuery){
18496                 this.lastQuery = q;
18497                 if(this.mode == 'local'){
18498                     this.selectedIndex = -1;
18499                     if(forceAll){
18500                         this.store.clearFilter();
18501                     }else{
18502                         
18503                         if(this.specialFilter){
18504                             this.fireEvent('specialfilter', this);
18505                             this.onLoad();
18506                             return;
18507                         }
18508                         
18509                         this.store.filter(this.displayField, q);
18510                     }
18511                     
18512                     this.store.fireEvent("datachanged", this.store);
18513                     
18514                     this.onLoad();
18515                     
18516                     
18517                 }else{
18518                     
18519                     this.store.baseParams[this.queryParam] = q;
18520                     
18521                     var options = {params : this.getParams(q)};
18522                     
18523                     if(this.loadNext){
18524                         options.add = true;
18525                         options.params.start = this.page * this.pageSize;
18526                     }
18527                     
18528                     this.store.load(options);
18529                     
18530                     /*
18531                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18532                      *  we should expand the list on onLoad
18533                      *  so command out it
18534                      */
18535 //                    this.expand();
18536                 }
18537             }else{
18538                 this.selectedIndex = -1;
18539                 this.onLoad();   
18540             }
18541         }
18542         
18543         this.loadNext = false;
18544     },
18545     
18546     // private
18547     getParams : function(q){
18548         var p = {};
18549         //p[this.queryParam] = q;
18550         
18551         if(this.pageSize){
18552             p.start = 0;
18553             p.limit = this.pageSize;
18554         }
18555         return p;
18556     },
18557
18558     /**
18559      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18560      */
18561     collapse : function(){
18562         if(!this.isExpanded()){
18563             return;
18564         }
18565         
18566         this.list.hide();
18567         
18568         this.hasFocus = false;
18569         
18570         if(this.tickable){
18571             this.okBtn.hide();
18572             this.cancelBtn.hide();
18573             this.trigger.show();
18574             
18575             if(this.editable){
18576                 this.tickableInputEl().dom.value = '';
18577                 this.tickableInputEl().blur();
18578             }
18579             
18580         }
18581         
18582         Roo.get(document).un('mousedown', this.collapseIf, this);
18583         Roo.get(document).un('mousewheel', this.collapseIf, this);
18584         if (!this.editable) {
18585             Roo.get(document).un('keydown', this.listKeyPress, this);
18586         }
18587         this.fireEvent('collapse', this);
18588         
18589         this.validate();
18590     },
18591
18592     // private
18593     collapseIf : function(e){
18594         var in_combo  = e.within(this.el);
18595         var in_list =  e.within(this.list);
18596         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18597         
18598         if (in_combo || in_list || is_list) {
18599             //e.stopPropagation();
18600             return;
18601         }
18602         
18603         if(this.tickable){
18604             this.onTickableFooterButtonClick(e, false, false);
18605         }
18606
18607         this.collapse();
18608         
18609     },
18610
18611     /**
18612      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18613      */
18614     expand : function(){
18615        
18616         if(this.isExpanded() || !this.hasFocus){
18617             return;
18618         }
18619         
18620         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18621         this.list.setWidth(lw);
18622         
18623         Roo.log('expand');
18624         
18625         this.list.show();
18626         
18627         this.restrictHeight();
18628         
18629         if(this.tickable){
18630             
18631             this.tickItems = Roo.apply([], this.item);
18632             
18633             this.okBtn.show();
18634             this.cancelBtn.show();
18635             this.trigger.hide();
18636             
18637             if(this.editable){
18638                 this.tickableInputEl().focus();
18639             }
18640             
18641         }
18642         
18643         Roo.get(document).on('mousedown', this.collapseIf, this);
18644         Roo.get(document).on('mousewheel', this.collapseIf, this);
18645         if (!this.editable) {
18646             Roo.get(document).on('keydown', this.listKeyPress, this);
18647         }
18648         
18649         this.fireEvent('expand', this);
18650     },
18651
18652     // private
18653     // Implements the default empty TriggerField.onTriggerClick function
18654     onTriggerClick : function(e)
18655     {
18656         Roo.log('trigger click');
18657         
18658         if(this.disabled || !this.triggerList){
18659             return;
18660         }
18661         
18662         this.page = 0;
18663         this.loadNext = false;
18664         
18665         if(this.isExpanded()){
18666             this.collapse();
18667             if (!this.blockFocus) {
18668                 this.inputEl().focus();
18669             }
18670             
18671         }else {
18672             this.hasFocus = true;
18673             if(this.triggerAction == 'all') {
18674                 this.doQuery(this.allQuery, true);
18675             } else {
18676                 this.doQuery(this.getRawValue());
18677             }
18678             if (!this.blockFocus) {
18679                 this.inputEl().focus();
18680             }
18681         }
18682     },
18683     
18684     onTickableTriggerClick : function(e)
18685     {
18686         if(this.disabled){
18687             return;
18688         }
18689         
18690         this.page = 0;
18691         this.loadNext = false;
18692         this.hasFocus = true;
18693         
18694         if(this.triggerAction == 'all') {
18695             this.doQuery(this.allQuery, true);
18696         } else {
18697             this.doQuery(this.getRawValue());
18698         }
18699     },
18700     
18701     onSearchFieldClick : function(e)
18702     {
18703         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18704             this.onTickableFooterButtonClick(e, false, false);
18705             return;
18706         }
18707         
18708         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18709             return;
18710         }
18711         
18712         this.page = 0;
18713         this.loadNext = false;
18714         this.hasFocus = true;
18715         
18716         if(this.triggerAction == 'all') {
18717             this.doQuery(this.allQuery, true);
18718         } else {
18719             this.doQuery(this.getRawValue());
18720         }
18721     },
18722     
18723     listKeyPress : function(e)
18724     {
18725         //Roo.log('listkeypress');
18726         // scroll to first matching element based on key pres..
18727         if (e.isSpecialKey()) {
18728             return false;
18729         }
18730         var k = String.fromCharCode(e.getKey()).toUpperCase();
18731         //Roo.log(k);
18732         var match  = false;
18733         var csel = this.view.getSelectedNodes();
18734         var cselitem = false;
18735         if (csel.length) {
18736             var ix = this.view.indexOf(csel[0]);
18737             cselitem  = this.store.getAt(ix);
18738             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18739                 cselitem = false;
18740             }
18741             
18742         }
18743         
18744         this.store.each(function(v) { 
18745             if (cselitem) {
18746                 // start at existing selection.
18747                 if (cselitem.id == v.id) {
18748                     cselitem = false;
18749                 }
18750                 return true;
18751             }
18752                 
18753             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18754                 match = this.store.indexOf(v);
18755                 return false;
18756             }
18757             return true;
18758         }, this);
18759         
18760         if (match === false) {
18761             return true; // no more action?
18762         }
18763         // scroll to?
18764         this.view.select(match);
18765         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18766         sn.scrollIntoView(sn.dom.parentNode, false);
18767     },
18768     
18769     onViewScroll : function(e, t){
18770         
18771         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){
18772             return;
18773         }
18774         
18775         this.hasQuery = true;
18776         
18777         this.loading = this.list.select('.loading', true).first();
18778         
18779         if(this.loading === null){
18780             this.list.createChild({
18781                 tag: 'div',
18782                 cls: 'loading roo-select2-more-results roo-select2-active',
18783                 html: 'Loading more results...'
18784             });
18785             
18786             this.loading = this.list.select('.loading', true).first();
18787             
18788             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18789             
18790             this.loading.hide();
18791         }
18792         
18793         this.loading.show();
18794         
18795         var _combo = this;
18796         
18797         this.page++;
18798         this.loadNext = true;
18799         
18800         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18801         
18802         return;
18803     },
18804     
18805     addItem : function(o)
18806     {   
18807         var dv = ''; // display value
18808         
18809         if (this.displayField) {
18810             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18811         } else {
18812             // this is an error condition!!!
18813             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18814         }
18815         
18816         if(!dv.length){
18817             return;
18818         }
18819         
18820         var choice = this.choices.createChild({
18821             tag: 'li',
18822             cls: 'roo-select2-search-choice',
18823             cn: [
18824                 {
18825                     tag: 'div',
18826                     html: dv
18827                 },
18828                 {
18829                     tag: 'a',
18830                     href: '#',
18831                     cls: 'roo-select2-search-choice-close fa fa-times',
18832                     tabindex: '-1'
18833                 }
18834             ]
18835             
18836         }, this.searchField);
18837         
18838         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18839         
18840         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18841         
18842         this.item.push(o);
18843         
18844         this.lastData = o;
18845         
18846         this.syncValue();
18847         
18848         this.inputEl().dom.value = '';
18849         
18850         this.validate();
18851     },
18852     
18853     onRemoveItem : function(e, _self, o)
18854     {
18855         e.preventDefault();
18856         
18857         this.lastItem = Roo.apply([], this.item);
18858         
18859         var index = this.item.indexOf(o.data) * 1;
18860         
18861         if( index < 0){
18862             Roo.log('not this item?!');
18863             return;
18864         }
18865         
18866         this.item.splice(index, 1);
18867         o.item.remove();
18868         
18869         this.syncValue();
18870         
18871         this.fireEvent('remove', this, e);
18872         
18873         this.validate();
18874         
18875     },
18876     
18877     syncValue : function()
18878     {
18879         if(!this.item.length){
18880             this.clearValue();
18881             return;
18882         }
18883             
18884         var value = [];
18885         var _this = this;
18886         Roo.each(this.item, function(i){
18887             if(_this.valueField){
18888                 value.push(i[_this.valueField]);
18889                 return;
18890             }
18891
18892             value.push(i);
18893         });
18894
18895         this.value = value.join(',');
18896
18897         if(this.hiddenField){
18898             this.hiddenField.dom.value = this.value;
18899         }
18900         
18901         this.store.fireEvent("datachanged", this.store);
18902         
18903         this.validate();
18904     },
18905     
18906     clearItem : function()
18907     {
18908         if(!this.multiple){
18909             return;
18910         }
18911         
18912         this.item = [];
18913         
18914         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18915            c.remove();
18916         });
18917         
18918         this.syncValue();
18919         
18920         this.validate();
18921         
18922         if(this.tickable && !Roo.isTouch){
18923             this.view.refresh();
18924         }
18925     },
18926     
18927     inputEl: function ()
18928     {
18929         if(Roo.isIOS && this.useNativeIOS){
18930             return this.el.select('select.roo-ios-select', true).first();
18931         }
18932         
18933         if(Roo.isTouch && this.mobileTouchView){
18934             return this.el.select('input.form-control',true).first();
18935         }
18936         
18937         if(this.tickable){
18938             return this.searchField;
18939         }
18940         
18941         return this.el.select('input.form-control',true).first();
18942     },
18943     
18944     onTickableFooterButtonClick : function(e, btn, el)
18945     {
18946         e.preventDefault();
18947         
18948         this.lastItem = Roo.apply([], this.item);
18949         
18950         if(btn && btn.name == 'cancel'){
18951             this.tickItems = Roo.apply([], this.item);
18952             this.collapse();
18953             return;
18954         }
18955         
18956         this.clearItem();
18957         
18958         var _this = this;
18959         
18960         Roo.each(this.tickItems, function(o){
18961             _this.addItem(o);
18962         });
18963         
18964         this.collapse();
18965         
18966     },
18967     
18968     validate : function()
18969     {
18970         if(this.getVisibilityEl().hasClass('hidden')){
18971             return true;
18972         }
18973         
18974         var v = this.getRawValue();
18975         
18976         if(this.multiple){
18977             v = this.getValue();
18978         }
18979         
18980         if(this.disabled || this.allowBlank || v.length){
18981             this.markValid();
18982             return true;
18983         }
18984         
18985         this.markInvalid();
18986         return false;
18987     },
18988     
18989     tickableInputEl : function()
18990     {
18991         if(!this.tickable || !this.editable){
18992             return this.inputEl();
18993         }
18994         
18995         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18996     },
18997     
18998     
18999     getAutoCreateTouchView : function()
19000     {
19001         var id = Roo.id();
19002         
19003         var cfg = {
19004             cls: 'form-group' //input-group
19005         };
19006         
19007         var input =  {
19008             tag: 'input',
19009             id : id,
19010             type : this.inputType,
19011             cls : 'form-control x-combo-noedit',
19012             autocomplete: 'new-password',
19013             placeholder : this.placeholder || '',
19014             readonly : true
19015         };
19016         
19017         if (this.name) {
19018             input.name = this.name;
19019         }
19020         
19021         if (this.size) {
19022             input.cls += ' input-' + this.size;
19023         }
19024         
19025         if (this.disabled) {
19026             input.disabled = true;
19027         }
19028         
19029         var inputblock = {
19030             cls : 'roo-combobox-wrap',
19031             cn : [
19032                 input
19033             ]
19034         };
19035         
19036         if(this.before){
19037             inputblock.cls += ' input-group';
19038             
19039             inputblock.cn.unshift({
19040                 tag :'span',
19041                 cls : 'input-group-addon input-group-prepend input-group-text',
19042                 html : this.before
19043             });
19044         }
19045         
19046         if(this.removable && !this.multiple){
19047             inputblock.cls += ' roo-removable';
19048             
19049             inputblock.cn.push({
19050                 tag: 'button',
19051                 html : 'x',
19052                 cls : 'roo-combo-removable-btn close'
19053             });
19054         }
19055
19056         if(this.hasFeedback && !this.allowBlank){
19057             
19058             inputblock.cls += ' has-feedback';
19059             
19060             inputblock.cn.push({
19061                 tag: 'span',
19062                 cls: 'glyphicon form-control-feedback'
19063             });
19064             
19065         }
19066         
19067         if (this.after) {
19068             
19069             inputblock.cls += (this.before) ? '' : ' input-group';
19070             
19071             inputblock.cn.push({
19072                 tag :'span',
19073                 cls : 'input-group-addon input-group-append input-group-text',
19074                 html : this.after
19075             });
19076         }
19077
19078         
19079         var ibwrap = inputblock;
19080         
19081         if(this.multiple){
19082             ibwrap = {
19083                 tag: 'ul',
19084                 cls: 'roo-select2-choices',
19085                 cn:[
19086                     {
19087                         tag: 'li',
19088                         cls: 'roo-select2-search-field',
19089                         cn: [
19090
19091                             inputblock
19092                         ]
19093                     }
19094                 ]
19095             };
19096         
19097             
19098         }
19099         
19100         var combobox = {
19101             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19102             cn: [
19103                 {
19104                     tag: 'input',
19105                     type : 'hidden',
19106                     cls: 'form-hidden-field'
19107                 },
19108                 ibwrap
19109             ]
19110         };
19111         
19112         if(!this.multiple && this.showToggleBtn){
19113             
19114             var caret = {
19115                 cls: 'caret'
19116             };
19117             
19118             if (this.caret != false) {
19119                 caret = {
19120                      tag: 'i',
19121                      cls: 'fa fa-' + this.caret
19122                 };
19123                 
19124             }
19125             
19126             combobox.cn.push({
19127                 tag :'span',
19128                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19129                 cn : [
19130                     Roo.bootstrap.version == 3 ? caret : '',
19131                     {
19132                         tag: 'span',
19133                         cls: 'combobox-clear',
19134                         cn  : [
19135                             {
19136                                 tag : 'i',
19137                                 cls: 'icon-remove'
19138                             }
19139                         ]
19140                     }
19141                 ]
19142
19143             })
19144         }
19145         
19146         if(this.multiple){
19147             combobox.cls += ' roo-select2-container-multi';
19148         }
19149         
19150         var required =  this.allowBlank ?  {
19151                     tag : 'i',
19152                     style: 'display: none'
19153                 } : {
19154                    tag : 'i',
19155                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19156                    tooltip : 'This field is required'
19157                 };
19158         
19159         var align = this.labelAlign || this.parentLabelAlign();
19160         
19161         if (align ==='left' && this.fieldLabel.length) {
19162
19163             cfg.cn = [
19164                 required,
19165                 {
19166                     tag: 'label',
19167                     cls : 'control-label col-form-label',
19168                     html : this.fieldLabel
19169
19170                 },
19171                 {
19172                     cls : 'roo-combobox-wrap ', 
19173                     cn: [
19174                         combobox
19175                     ]
19176                 }
19177             ];
19178             
19179             var labelCfg = cfg.cn[1];
19180             var contentCfg = cfg.cn[2];
19181             
19182
19183             if(this.indicatorpos == 'right'){
19184                 cfg.cn = [
19185                     {
19186                         tag: 'label',
19187                         'for' :  id,
19188                         cls : 'control-label col-form-label',
19189                         cn : [
19190                             {
19191                                 tag : 'span',
19192                                 html : this.fieldLabel
19193                             },
19194                             required
19195                         ]
19196                     },
19197                     {
19198                         cls : "roo-combobox-wrap ",
19199                         cn: [
19200                             combobox
19201                         ]
19202                     }
19203
19204                 ];
19205                 
19206                 labelCfg = cfg.cn[0];
19207                 contentCfg = cfg.cn[1];
19208             }
19209             
19210            
19211             
19212             if(this.labelWidth > 12){
19213                 labelCfg.style = "width: " + this.labelWidth + 'px';
19214             }
19215            
19216             if(this.labelWidth < 13 && this.labelmd == 0){
19217                 this.labelmd = this.labelWidth;
19218             }
19219             
19220             if(this.labellg > 0){
19221                 labelCfg.cls += ' col-lg-' + this.labellg;
19222                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19223             }
19224             
19225             if(this.labelmd > 0){
19226                 labelCfg.cls += ' col-md-' + this.labelmd;
19227                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19228             }
19229             
19230             if(this.labelsm > 0){
19231                 labelCfg.cls += ' col-sm-' + this.labelsm;
19232                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19233             }
19234             
19235             if(this.labelxs > 0){
19236                 labelCfg.cls += ' col-xs-' + this.labelxs;
19237                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19238             }
19239                 
19240                 
19241         } else if ( this.fieldLabel.length) {
19242             cfg.cn = [
19243                required,
19244                 {
19245                     tag: 'label',
19246                     cls : 'control-label',
19247                     html : this.fieldLabel
19248
19249                 },
19250                 {
19251                     cls : '', 
19252                     cn: [
19253                         combobox
19254                     ]
19255                 }
19256             ];
19257             
19258             if(this.indicatorpos == 'right'){
19259                 cfg.cn = [
19260                     {
19261                         tag: 'label',
19262                         cls : 'control-label',
19263                         html : this.fieldLabel,
19264                         cn : [
19265                             required
19266                         ]
19267                     },
19268                     {
19269                         cls : '', 
19270                         cn: [
19271                             combobox
19272                         ]
19273                     }
19274                 ];
19275             }
19276         } else {
19277             cfg.cn = combobox;    
19278         }
19279         
19280         
19281         var settings = this;
19282         
19283         ['xs','sm','md','lg'].map(function(size){
19284             if (settings[size]) {
19285                 cfg.cls += ' col-' + size + '-' + settings[size];
19286             }
19287         });
19288         
19289         return cfg;
19290     },
19291     
19292     initTouchView : function()
19293     {
19294         this.renderTouchView();
19295         
19296         this.touchViewEl.on('scroll', function(){
19297             this.el.dom.scrollTop = 0;
19298         }, this);
19299         
19300         this.originalValue = this.getValue();
19301         
19302         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19303         
19304         this.inputEl().on("click", this.showTouchView, this);
19305         if (this.triggerEl) {
19306             this.triggerEl.on("click", this.showTouchView, this);
19307         }
19308         
19309         
19310         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19311         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19312         
19313         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19314         
19315         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19316         this.store.on('load', this.onTouchViewLoad, this);
19317         this.store.on('loadexception', this.onTouchViewLoadException, this);
19318         
19319         if(this.hiddenName){
19320             
19321             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19322             
19323             this.hiddenField.dom.value =
19324                 this.hiddenValue !== undefined ? this.hiddenValue :
19325                 this.value !== undefined ? this.value : '';
19326         
19327             this.el.dom.removeAttribute('name');
19328             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19329         }
19330         
19331         if(this.multiple){
19332             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19333             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19334         }
19335         
19336         if(this.removable && !this.multiple){
19337             var close = this.closeTriggerEl();
19338             if(close){
19339                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19340                 close.on('click', this.removeBtnClick, this, close);
19341             }
19342         }
19343         /*
19344          * fix the bug in Safari iOS8
19345          */
19346         this.inputEl().on("focus", function(e){
19347             document.activeElement.blur();
19348         }, this);
19349         
19350         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19351         
19352         return;
19353         
19354         
19355     },
19356     
19357     renderTouchView : function()
19358     {
19359         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19360         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19361         
19362         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19363         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19364         
19365         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19366         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19367         this.touchViewBodyEl.setStyle('overflow', 'auto');
19368         
19369         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19370         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19371         
19372         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19373         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19374         
19375     },
19376     
19377     showTouchView : function()
19378     {
19379         if(this.disabled){
19380             return;
19381         }
19382         
19383         this.touchViewHeaderEl.hide();
19384
19385         if(this.modalTitle.length){
19386             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19387             this.touchViewHeaderEl.show();
19388         }
19389
19390         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19391         this.touchViewEl.show();
19392
19393         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19394         
19395         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19396         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19397
19398         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19399
19400         if(this.modalTitle.length){
19401             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19402         }
19403         
19404         this.touchViewBodyEl.setHeight(bodyHeight);
19405
19406         if(this.animate){
19407             var _this = this;
19408             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19409         }else{
19410             this.touchViewEl.addClass(['in','show']);
19411         }
19412         
19413         if(this._touchViewMask){
19414             Roo.get(document.body).addClass("x-body-masked");
19415             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19416             this._touchViewMask.setStyle('z-index', 10000);
19417             this._touchViewMask.addClass('show');
19418         }
19419         
19420         this.doTouchViewQuery();
19421         
19422     },
19423     
19424     hideTouchView : function()
19425     {
19426         this.touchViewEl.removeClass(['in','show']);
19427
19428         if(this.animate){
19429             var _this = this;
19430             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19431         }else{
19432             this.touchViewEl.setStyle('display', 'none');
19433         }
19434         
19435         if(this._touchViewMask){
19436             this._touchViewMask.removeClass('show');
19437             Roo.get(document.body).removeClass("x-body-masked");
19438         }
19439     },
19440     
19441     setTouchViewValue : function()
19442     {
19443         if(this.multiple){
19444             this.clearItem();
19445         
19446             var _this = this;
19447
19448             Roo.each(this.tickItems, function(o){
19449                 this.addItem(o);
19450             }, this);
19451         }
19452         
19453         this.hideTouchView();
19454     },
19455     
19456     doTouchViewQuery : function()
19457     {
19458         var qe = {
19459             query: '',
19460             forceAll: true,
19461             combo: this,
19462             cancel:false
19463         };
19464         
19465         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19466             return false;
19467         }
19468         
19469         if(!this.alwaysQuery || this.mode == 'local'){
19470             this.onTouchViewLoad();
19471             return;
19472         }
19473         
19474         this.store.load();
19475     },
19476     
19477     onTouchViewBeforeLoad : function(combo,opts)
19478     {
19479         return;
19480     },
19481
19482     // private
19483     onTouchViewLoad : function()
19484     {
19485         if(this.store.getCount() < 1){
19486             this.onTouchViewEmptyResults();
19487             return;
19488         }
19489         
19490         this.clearTouchView();
19491         
19492         var rawValue = this.getRawValue();
19493         
19494         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19495         
19496         this.tickItems = [];
19497         
19498         this.store.data.each(function(d, rowIndex){
19499             var row = this.touchViewListGroup.createChild(template);
19500             
19501             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19502                 row.addClass(d.data.cls);
19503             }
19504             
19505             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19506                 var cfg = {
19507                     data : d.data,
19508                     html : d.data[this.displayField]
19509                 };
19510                 
19511                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19512                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19513                 }
19514             }
19515             row.removeClass('selected');
19516             if(!this.multiple && this.valueField &&
19517                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19518             {
19519                 // radio buttons..
19520                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19521                 row.addClass('selected');
19522             }
19523             
19524             if(this.multiple && this.valueField &&
19525                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19526             {
19527                 
19528                 // checkboxes...
19529                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19530                 this.tickItems.push(d.data);
19531             }
19532             
19533             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19534             
19535         }, this);
19536         
19537         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19538         
19539         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19540
19541         if(this.modalTitle.length){
19542             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19543         }
19544
19545         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19546         
19547         if(this.mobile_restrict_height && listHeight < bodyHeight){
19548             this.touchViewBodyEl.setHeight(listHeight);
19549         }
19550         
19551         var _this = this;
19552         
19553         if(firstChecked && listHeight > bodyHeight){
19554             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19555         }
19556         
19557     },
19558     
19559     onTouchViewLoadException : function()
19560     {
19561         this.hideTouchView();
19562     },
19563     
19564     onTouchViewEmptyResults : function()
19565     {
19566         this.clearTouchView();
19567         
19568         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19569         
19570         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19571         
19572     },
19573     
19574     clearTouchView : function()
19575     {
19576         this.touchViewListGroup.dom.innerHTML = '';
19577     },
19578     
19579     onTouchViewClick : function(e, el, o)
19580     {
19581         e.preventDefault();
19582         
19583         var row = o.row;
19584         var rowIndex = o.rowIndex;
19585         
19586         var r = this.store.getAt(rowIndex);
19587         
19588         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19589             
19590             if(!this.multiple){
19591                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19592                     c.dom.removeAttribute('checked');
19593                 }, this);
19594
19595                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19596
19597                 this.setFromData(r.data);
19598
19599                 var close = this.closeTriggerEl();
19600
19601                 if(close){
19602                     close.show();
19603                 }
19604
19605                 this.hideTouchView();
19606
19607                 this.fireEvent('select', this, r, rowIndex);
19608
19609                 return;
19610             }
19611
19612             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19613                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19614                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19615                 return;
19616             }
19617
19618             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19619             this.addItem(r.data);
19620             this.tickItems.push(r.data);
19621         }
19622     },
19623     
19624     getAutoCreateNativeIOS : function()
19625     {
19626         var cfg = {
19627             cls: 'form-group' //input-group,
19628         };
19629         
19630         var combobox =  {
19631             tag: 'select',
19632             cls : 'roo-ios-select'
19633         };
19634         
19635         if (this.name) {
19636             combobox.name = this.name;
19637         }
19638         
19639         if (this.disabled) {
19640             combobox.disabled = true;
19641         }
19642         
19643         var settings = this;
19644         
19645         ['xs','sm','md','lg'].map(function(size){
19646             if (settings[size]) {
19647                 cfg.cls += ' col-' + size + '-' + settings[size];
19648             }
19649         });
19650         
19651         cfg.cn = combobox;
19652         
19653         return cfg;
19654         
19655     },
19656     
19657     initIOSView : function()
19658     {
19659         this.store.on('load', this.onIOSViewLoad, this);
19660         
19661         return;
19662     },
19663     
19664     onIOSViewLoad : function()
19665     {
19666         if(this.store.getCount() < 1){
19667             return;
19668         }
19669         
19670         this.clearIOSView();
19671         
19672         if(this.allowBlank) {
19673             
19674             var default_text = '-- SELECT --';
19675             
19676             if(this.placeholder.length){
19677                 default_text = this.placeholder;
19678             }
19679             
19680             if(this.emptyTitle.length){
19681                 default_text += ' - ' + this.emptyTitle + ' -';
19682             }
19683             
19684             var opt = this.inputEl().createChild({
19685                 tag: 'option',
19686                 value : 0,
19687                 html : default_text
19688             });
19689             
19690             var o = {};
19691             o[this.valueField] = 0;
19692             o[this.displayField] = default_text;
19693             
19694             this.ios_options.push({
19695                 data : o,
19696                 el : opt
19697             });
19698             
19699         }
19700         
19701         this.store.data.each(function(d, rowIndex){
19702             
19703             var html = '';
19704             
19705             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19706                 html = d.data[this.displayField];
19707             }
19708             
19709             var value = '';
19710             
19711             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19712                 value = d.data[this.valueField];
19713             }
19714             
19715             var option = {
19716                 tag: 'option',
19717                 value : value,
19718                 html : html
19719             };
19720             
19721             if(this.value == d.data[this.valueField]){
19722                 option['selected'] = true;
19723             }
19724             
19725             var opt = this.inputEl().createChild(option);
19726             
19727             this.ios_options.push({
19728                 data : d.data,
19729                 el : opt
19730             });
19731             
19732         }, this);
19733         
19734         this.inputEl().on('change', function(){
19735            this.fireEvent('select', this);
19736         }, this);
19737         
19738     },
19739     
19740     clearIOSView: function()
19741     {
19742         this.inputEl().dom.innerHTML = '';
19743         
19744         this.ios_options = [];
19745     },
19746     
19747     setIOSValue: function(v)
19748     {
19749         this.value = v;
19750         
19751         if(!this.ios_options){
19752             return;
19753         }
19754         
19755         Roo.each(this.ios_options, function(opts){
19756            
19757            opts.el.dom.removeAttribute('selected');
19758            
19759            if(opts.data[this.valueField] != v){
19760                return;
19761            }
19762            
19763            opts.el.dom.setAttribute('selected', true);
19764            
19765         }, this);
19766     }
19767
19768     /** 
19769     * @cfg {Boolean} grow 
19770     * @hide 
19771     */
19772     /** 
19773     * @cfg {Number} growMin 
19774     * @hide 
19775     */
19776     /** 
19777     * @cfg {Number} growMax 
19778     * @hide 
19779     */
19780     /**
19781      * @hide
19782      * @method autoSize
19783      */
19784 });
19785
19786 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19787     
19788     header : {
19789         tag: 'div',
19790         cls: 'modal-header',
19791         cn: [
19792             {
19793                 tag: 'h4',
19794                 cls: 'modal-title'
19795             }
19796         ]
19797     },
19798     
19799     body : {
19800         tag: 'div',
19801         cls: 'modal-body',
19802         cn: [
19803             {
19804                 tag: 'ul',
19805                 cls: 'list-group'
19806             }
19807         ]
19808     },
19809     
19810     listItemRadio : {
19811         tag: 'li',
19812         cls: 'list-group-item',
19813         cn: [
19814             {
19815                 tag: 'span',
19816                 cls: 'roo-combobox-list-group-item-value'
19817             },
19818             {
19819                 tag: 'div',
19820                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19821                 cn: [
19822                     {
19823                         tag: 'input',
19824                         type: 'radio'
19825                     },
19826                     {
19827                         tag: 'label'
19828                     }
19829                 ]
19830             }
19831         ]
19832     },
19833     
19834     listItemCheckbox : {
19835         tag: 'li',
19836         cls: 'list-group-item',
19837         cn: [
19838             {
19839                 tag: 'span',
19840                 cls: 'roo-combobox-list-group-item-value'
19841             },
19842             {
19843                 tag: 'div',
19844                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19845                 cn: [
19846                     {
19847                         tag: 'input',
19848                         type: 'checkbox'
19849                     },
19850                     {
19851                         tag: 'label'
19852                     }
19853                 ]
19854             }
19855         ]
19856     },
19857     
19858     emptyResult : {
19859         tag: 'div',
19860         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19861     },
19862     
19863     footer : {
19864         tag: 'div',
19865         cls: 'modal-footer',
19866         cn: [
19867             {
19868                 tag: 'div',
19869                 cls: 'row',
19870                 cn: [
19871                     {
19872                         tag: 'div',
19873                         cls: 'col-xs-6 text-left',
19874                         cn: {
19875                             tag: 'button',
19876                             cls: 'btn btn-danger roo-touch-view-cancel',
19877                             html: 'Cancel'
19878                         }
19879                     },
19880                     {
19881                         tag: 'div',
19882                         cls: 'col-xs-6 text-right',
19883                         cn: {
19884                             tag: 'button',
19885                             cls: 'btn btn-success roo-touch-view-ok',
19886                             html: 'OK'
19887                         }
19888                     }
19889                 ]
19890             }
19891         ]
19892         
19893     }
19894 });
19895
19896 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19897     
19898     touchViewTemplate : {
19899         tag: 'div',
19900         cls: 'modal fade roo-combobox-touch-view',
19901         cn: [
19902             {
19903                 tag: 'div',
19904                 cls: 'modal-dialog',
19905                 style : 'position:fixed', // we have to fix position....
19906                 cn: [
19907                     {
19908                         tag: 'div',
19909                         cls: 'modal-content',
19910                         cn: [
19911                             Roo.bootstrap.form.ComboBox.header,
19912                             Roo.bootstrap.form.ComboBox.body,
19913                             Roo.bootstrap.form.ComboBox.footer
19914                         ]
19915                     }
19916                 ]
19917             }
19918         ]
19919     }
19920 });/*
19921  * Based on:
19922  * Ext JS Library 1.1.1
19923  * Copyright(c) 2006-2007, Ext JS, LLC.
19924  *
19925  * Originally Released Under LGPL - original licence link has changed is not relivant.
19926  *
19927  * Fork - LGPL
19928  * <script type="text/javascript">
19929  */
19930
19931 /**
19932  * @class Roo.View
19933  * @extends Roo.util.Observable
19934  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19935  * This class also supports single and multi selection modes. <br>
19936  * Create a data model bound view:
19937  <pre><code>
19938  var store = new Roo.data.Store(...);
19939
19940  var view = new Roo.View({
19941     el : "my-element",
19942     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19943  
19944     singleSelect: true,
19945     selectedClass: "ydataview-selected",
19946     store: store
19947  });
19948
19949  // listen for node click?
19950  view.on("click", function(vw, index, node, e){
19951  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19952  });
19953
19954  // load XML data
19955  dataModel.load("foobar.xml");
19956  </code></pre>
19957  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19958  * <br><br>
19959  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19960  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19961  * 
19962  * Note: old style constructor is still suported (container, template, config)
19963  * 
19964  * @constructor
19965  * Create a new View
19966  * @param {Object} config The config object
19967  * 
19968  */
19969 Roo.View = function(config, depreciated_tpl, depreciated_config){
19970     
19971     this.parent = false;
19972     
19973     if (typeof(depreciated_tpl) == 'undefined') {
19974         // new way.. - universal constructor.
19975         Roo.apply(this, config);
19976         this.el  = Roo.get(this.el);
19977     } else {
19978         // old format..
19979         this.el  = Roo.get(config);
19980         this.tpl = depreciated_tpl;
19981         Roo.apply(this, depreciated_config);
19982     }
19983     this.wrapEl  = this.el.wrap().wrap();
19984     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19985     
19986     
19987     if(typeof(this.tpl) == "string"){
19988         this.tpl = new Roo.Template(this.tpl);
19989     } else {
19990         // support xtype ctors..
19991         this.tpl = new Roo.factory(this.tpl, Roo);
19992     }
19993     
19994     
19995     this.tpl.compile();
19996     
19997     /** @private */
19998     this.addEvents({
19999         /**
20000          * @event beforeclick
20001          * Fires before a click is processed. Returns false to cancel the default action.
20002          * @param {Roo.View} this
20003          * @param {Number} index The index of the target node
20004          * @param {HTMLElement} node The target node
20005          * @param {Roo.EventObject} e The raw event object
20006          */
20007             "beforeclick" : true,
20008         /**
20009          * @event click
20010          * Fires when a template node is clicked.
20011          * @param {Roo.View} this
20012          * @param {Number} index The index of the target node
20013          * @param {HTMLElement} node The target node
20014          * @param {Roo.EventObject} e The raw event object
20015          */
20016             "click" : true,
20017         /**
20018          * @event dblclick
20019          * Fires when a template node is double clicked.
20020          * @param {Roo.View} this
20021          * @param {Number} index The index of the target node
20022          * @param {HTMLElement} node The target node
20023          * @param {Roo.EventObject} e The raw event object
20024          */
20025             "dblclick" : true,
20026         /**
20027          * @event contextmenu
20028          * Fires when a template node is right clicked.
20029          * @param {Roo.View} this
20030          * @param {Number} index The index of the target node
20031          * @param {HTMLElement} node The target node
20032          * @param {Roo.EventObject} e The raw event object
20033          */
20034             "contextmenu" : true,
20035         /**
20036          * @event selectionchange
20037          * Fires when the selected nodes change.
20038          * @param {Roo.View} this
20039          * @param {Array} selections Array of the selected nodes
20040          */
20041             "selectionchange" : true,
20042     
20043         /**
20044          * @event beforeselect
20045          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20046          * @param {Roo.View} this
20047          * @param {HTMLElement} node The node to be selected
20048          * @param {Array} selections Array of currently selected nodes
20049          */
20050             "beforeselect" : true,
20051         /**
20052          * @event preparedata
20053          * Fires on every row to render, to allow you to change the data.
20054          * @param {Roo.View} this
20055          * @param {Object} data to be rendered (change this)
20056          */
20057           "preparedata" : true
20058           
20059           
20060         });
20061
20062
20063
20064     this.el.on({
20065         "click": this.onClick,
20066         "dblclick": this.onDblClick,
20067         "contextmenu": this.onContextMenu,
20068         scope:this
20069     });
20070
20071     this.selections = [];
20072     this.nodes = [];
20073     this.cmp = new Roo.CompositeElementLite([]);
20074     if(this.store){
20075         this.store = Roo.factory(this.store, Roo.data);
20076         this.setStore(this.store, true);
20077     }
20078     
20079     if ( this.footer && this.footer.xtype) {
20080            
20081          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20082         
20083         this.footer.dataSource = this.store;
20084         this.footer.container = fctr;
20085         this.footer = Roo.factory(this.footer, Roo);
20086         fctr.insertFirst(this.el);
20087         
20088         // this is a bit insane - as the paging toolbar seems to detach the el..
20089 //        dom.parentNode.parentNode.parentNode
20090          // they get detached?
20091     }
20092     
20093     
20094     Roo.View.superclass.constructor.call(this);
20095     
20096     
20097 };
20098
20099 Roo.extend(Roo.View, Roo.util.Observable, {
20100     
20101      /**
20102      * @cfg {Roo.data.Store} store Data store to load data from.
20103      */
20104     store : false,
20105     
20106     /**
20107      * @cfg {String|Roo.Element} el The container element.
20108      */
20109     el : '',
20110     
20111     /**
20112      * @cfg {String|Roo.Template} tpl The template used by this View 
20113      */
20114     tpl : false,
20115     /**
20116      * @cfg {String} dataName the named area of the template to use as the data area
20117      *                          Works with domtemplates roo-name="name"
20118      */
20119     dataName: false,
20120     /**
20121      * @cfg {String} selectedClass The css class to add to selected nodes
20122      */
20123     selectedClass : "x-view-selected",
20124      /**
20125      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20126      */
20127     emptyText : "",
20128     
20129     /**
20130      * @cfg {String} text to display on mask (default Loading)
20131      */
20132     mask : false,
20133     /**
20134      * @cfg {Boolean} multiSelect Allow multiple selection
20135      */
20136     multiSelect : false,
20137     /**
20138      * @cfg {Boolean} singleSelect Allow single selection
20139      */
20140     singleSelect:  false,
20141     
20142     /**
20143      * @cfg {Boolean} toggleSelect - selecting 
20144      */
20145     toggleSelect : false,
20146     
20147     /**
20148      * @cfg {Boolean} tickable - selecting 
20149      */
20150     tickable : false,
20151     
20152     /**
20153      * Returns the element this view is bound to.
20154      * @return {Roo.Element}
20155      */
20156     getEl : function(){
20157         return this.wrapEl;
20158     },
20159     
20160     
20161
20162     /**
20163      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20164      */
20165     refresh : function(){
20166         //Roo.log('refresh');
20167         var t = this.tpl;
20168         
20169         // if we are using something like 'domtemplate', then
20170         // the what gets used is:
20171         // t.applySubtemplate(NAME, data, wrapping data..)
20172         // the outer template then get' applied with
20173         //     the store 'extra data'
20174         // and the body get's added to the
20175         //      roo-name="data" node?
20176         //      <span class='roo-tpl-{name}'></span> ?????
20177         
20178         
20179         
20180         this.clearSelections();
20181         this.el.update("");
20182         var html = [];
20183         var records = this.store.getRange();
20184         if(records.length < 1) {
20185             
20186             // is this valid??  = should it render a template??
20187             
20188             this.el.update(this.emptyText);
20189             return;
20190         }
20191         var el = this.el;
20192         if (this.dataName) {
20193             this.el.update(t.apply(this.store.meta)); //????
20194             el = this.el.child('.roo-tpl-' + this.dataName);
20195         }
20196         
20197         for(var i = 0, len = records.length; i < len; i++){
20198             var data = this.prepareData(records[i].data, i, records[i]);
20199             this.fireEvent("preparedata", this, data, i, records[i]);
20200             
20201             var d = Roo.apply({}, data);
20202             
20203             if(this.tickable){
20204                 Roo.apply(d, {'roo-id' : Roo.id()});
20205                 
20206                 var _this = this;
20207             
20208                 Roo.each(this.parent.item, function(item){
20209                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20210                         return;
20211                     }
20212                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20213                 });
20214             }
20215             
20216             html[html.length] = Roo.util.Format.trim(
20217                 this.dataName ?
20218                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20219                     t.apply(d)
20220             );
20221         }
20222         
20223         
20224         
20225         el.update(html.join(""));
20226         this.nodes = el.dom.childNodes;
20227         this.updateIndexes(0);
20228     },
20229     
20230
20231     /**
20232      * Function to override to reformat the data that is sent to
20233      * the template for each node.
20234      * DEPRICATED - use the preparedata event handler.
20235      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20236      * a JSON object for an UpdateManager bound view).
20237      */
20238     prepareData : function(data, index, record)
20239     {
20240         this.fireEvent("preparedata", this, data, index, record);
20241         return data;
20242     },
20243
20244     onUpdate : function(ds, record){
20245         // Roo.log('on update');   
20246         this.clearSelections();
20247         var index = this.store.indexOf(record);
20248         var n = this.nodes[index];
20249         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20250         n.parentNode.removeChild(n);
20251         this.updateIndexes(index, index);
20252     },
20253
20254     
20255     
20256 // --------- FIXME     
20257     onAdd : function(ds, records, index)
20258     {
20259         //Roo.log(['on Add', ds, records, index] );        
20260         this.clearSelections();
20261         if(this.nodes.length == 0){
20262             this.refresh();
20263             return;
20264         }
20265         var n = this.nodes[index];
20266         for(var i = 0, len = records.length; i < len; i++){
20267             var d = this.prepareData(records[i].data, i, records[i]);
20268             if(n){
20269                 this.tpl.insertBefore(n, d);
20270             }else{
20271                 
20272                 this.tpl.append(this.el, d);
20273             }
20274         }
20275         this.updateIndexes(index);
20276     },
20277
20278     onRemove : function(ds, record, index){
20279        // Roo.log('onRemove');
20280         this.clearSelections();
20281         var el = this.dataName  ?
20282             this.el.child('.roo-tpl-' + this.dataName) :
20283             this.el; 
20284         
20285         el.dom.removeChild(this.nodes[index]);
20286         this.updateIndexes(index);
20287     },
20288
20289     /**
20290      * Refresh an individual node.
20291      * @param {Number} index
20292      */
20293     refreshNode : function(index){
20294         this.onUpdate(this.store, this.store.getAt(index));
20295     },
20296
20297     updateIndexes : function(startIndex, endIndex){
20298         var ns = this.nodes;
20299         startIndex = startIndex || 0;
20300         endIndex = endIndex || ns.length - 1;
20301         for(var i = startIndex; i <= endIndex; i++){
20302             ns[i].nodeIndex = i;
20303         }
20304     },
20305
20306     /**
20307      * Changes the data store this view uses and refresh the view.
20308      * @param {Store} store
20309      */
20310     setStore : function(store, initial){
20311         if(!initial && this.store){
20312             this.store.un("datachanged", this.refresh);
20313             this.store.un("add", this.onAdd);
20314             this.store.un("remove", this.onRemove);
20315             this.store.un("update", this.onUpdate);
20316             this.store.un("clear", this.refresh);
20317             this.store.un("beforeload", this.onBeforeLoad);
20318             this.store.un("load", this.onLoad);
20319             this.store.un("loadexception", this.onLoad);
20320         }
20321         if(store){
20322           
20323             store.on("datachanged", this.refresh, this);
20324             store.on("add", this.onAdd, this);
20325             store.on("remove", this.onRemove, this);
20326             store.on("update", this.onUpdate, this);
20327             store.on("clear", this.refresh, this);
20328             store.on("beforeload", this.onBeforeLoad, this);
20329             store.on("load", this.onLoad, this);
20330             store.on("loadexception", this.onLoad, this);
20331         }
20332         
20333         if(store){
20334             this.refresh();
20335         }
20336     },
20337     /**
20338      * onbeforeLoad - masks the loading area.
20339      *
20340      */
20341     onBeforeLoad : function(store,opts)
20342     {
20343          //Roo.log('onBeforeLoad');   
20344         if (!opts.add) {
20345             this.el.update("");
20346         }
20347         this.el.mask(this.mask ? this.mask : "Loading" ); 
20348     },
20349     onLoad : function ()
20350     {
20351         this.el.unmask();
20352     },
20353     
20354
20355     /**
20356      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20357      * @param {HTMLElement} node
20358      * @return {HTMLElement} The template node
20359      */
20360     findItemFromChild : function(node){
20361         var el = this.dataName  ?
20362             this.el.child('.roo-tpl-' + this.dataName,true) :
20363             this.el.dom; 
20364         
20365         if(!node || node.parentNode == el){
20366                     return node;
20367             }
20368             var p = node.parentNode;
20369             while(p && p != el){
20370             if(p.parentNode == el){
20371                 return p;
20372             }
20373             p = p.parentNode;
20374         }
20375             return null;
20376     },
20377
20378     /** @ignore */
20379     onClick : function(e){
20380         var item = this.findItemFromChild(e.getTarget());
20381         if(item){
20382             var index = this.indexOf(item);
20383             if(this.onItemClick(item, index, e) !== false){
20384                 this.fireEvent("click", this, index, item, e);
20385             }
20386         }else{
20387             this.clearSelections();
20388         }
20389     },
20390
20391     /** @ignore */
20392     onContextMenu : function(e){
20393         var item = this.findItemFromChild(e.getTarget());
20394         if(item){
20395             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20396         }
20397     },
20398
20399     /** @ignore */
20400     onDblClick : function(e){
20401         var item = this.findItemFromChild(e.getTarget());
20402         if(item){
20403             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20404         }
20405     },
20406
20407     onItemClick : function(item, index, e)
20408     {
20409         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20410             return false;
20411         }
20412         if (this.toggleSelect) {
20413             var m = this.isSelected(item) ? 'unselect' : 'select';
20414             //Roo.log(m);
20415             var _t = this;
20416             _t[m](item, true, false);
20417             return true;
20418         }
20419         if(this.multiSelect || this.singleSelect){
20420             if(this.multiSelect && e.shiftKey && this.lastSelection){
20421                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20422             }else{
20423                 this.select(item, this.multiSelect && e.ctrlKey);
20424                 this.lastSelection = item;
20425             }
20426             
20427             if(!this.tickable){
20428                 e.preventDefault();
20429             }
20430             
20431         }
20432         return true;
20433     },
20434
20435     /**
20436      * Get the number of selected nodes.
20437      * @return {Number}
20438      */
20439     getSelectionCount : function(){
20440         return this.selections.length;
20441     },
20442
20443     /**
20444      * Get the currently selected nodes.
20445      * @return {Array} An array of HTMLElements
20446      */
20447     getSelectedNodes : function(){
20448         return this.selections;
20449     },
20450
20451     /**
20452      * Get the indexes of the selected nodes.
20453      * @return {Array}
20454      */
20455     getSelectedIndexes : function(){
20456         var indexes = [], s = this.selections;
20457         for(var i = 0, len = s.length; i < len; i++){
20458             indexes.push(s[i].nodeIndex);
20459         }
20460         return indexes;
20461     },
20462
20463     /**
20464      * Clear all selections
20465      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20466      */
20467     clearSelections : function(suppressEvent){
20468         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20469             this.cmp.elements = this.selections;
20470             this.cmp.removeClass(this.selectedClass);
20471             this.selections = [];
20472             if(!suppressEvent){
20473                 this.fireEvent("selectionchange", this, this.selections);
20474             }
20475         }
20476     },
20477
20478     /**
20479      * Returns true if the passed node is selected
20480      * @param {HTMLElement/Number} node The node or node index
20481      * @return {Boolean}
20482      */
20483     isSelected : function(node){
20484         var s = this.selections;
20485         if(s.length < 1){
20486             return false;
20487         }
20488         node = this.getNode(node);
20489         return s.indexOf(node) !== -1;
20490     },
20491
20492     /**
20493      * Selects nodes.
20494      * @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
20495      * @param {Boolean} keepExisting (optional) true to keep existing selections
20496      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20497      */
20498     select : function(nodeInfo, keepExisting, suppressEvent){
20499         if(nodeInfo instanceof Array){
20500             if(!keepExisting){
20501                 this.clearSelections(true);
20502             }
20503             for(var i = 0, len = nodeInfo.length; i < len; i++){
20504                 this.select(nodeInfo[i], true, true);
20505             }
20506             return;
20507         } 
20508         var node = this.getNode(nodeInfo);
20509         if(!node || this.isSelected(node)){
20510             return; // already selected.
20511         }
20512         if(!keepExisting){
20513             this.clearSelections(true);
20514         }
20515         
20516         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20517             Roo.fly(node).addClass(this.selectedClass);
20518             this.selections.push(node);
20519             if(!suppressEvent){
20520                 this.fireEvent("selectionchange", this, this.selections);
20521             }
20522         }
20523         
20524         
20525     },
20526       /**
20527      * Unselects nodes.
20528      * @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
20529      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20530      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20531      */
20532     unselect : function(nodeInfo, keepExisting, suppressEvent)
20533     {
20534         if(nodeInfo instanceof Array){
20535             Roo.each(this.selections, function(s) {
20536                 this.unselect(s, nodeInfo);
20537             }, this);
20538             return;
20539         }
20540         var node = this.getNode(nodeInfo);
20541         if(!node || !this.isSelected(node)){
20542             //Roo.log("not selected");
20543             return; // not selected.
20544         }
20545         // fireevent???
20546         var ns = [];
20547         Roo.each(this.selections, function(s) {
20548             if (s == node ) {
20549                 Roo.fly(node).removeClass(this.selectedClass);
20550
20551                 return;
20552             }
20553             ns.push(s);
20554         },this);
20555         
20556         this.selections= ns;
20557         this.fireEvent("selectionchange", this, this.selections);
20558     },
20559
20560     /**
20561      * Gets a template node.
20562      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20563      * @return {HTMLElement} The node or null if it wasn't found
20564      */
20565     getNode : function(nodeInfo){
20566         if(typeof nodeInfo == "string"){
20567             return document.getElementById(nodeInfo);
20568         }else if(typeof nodeInfo == "number"){
20569             return this.nodes[nodeInfo];
20570         }
20571         return nodeInfo;
20572     },
20573
20574     /**
20575      * Gets a range template nodes.
20576      * @param {Number} startIndex
20577      * @param {Number} endIndex
20578      * @return {Array} An array of nodes
20579      */
20580     getNodes : function(start, end){
20581         var ns = this.nodes;
20582         start = start || 0;
20583         end = typeof end == "undefined" ? ns.length - 1 : end;
20584         var nodes = [];
20585         if(start <= end){
20586             for(var i = start; i <= end; i++){
20587                 nodes.push(ns[i]);
20588             }
20589         } else{
20590             for(var i = start; i >= end; i--){
20591                 nodes.push(ns[i]);
20592             }
20593         }
20594         return nodes;
20595     },
20596
20597     /**
20598      * Finds the index of the passed node
20599      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20600      * @return {Number} The index of the node or -1
20601      */
20602     indexOf : function(node){
20603         node = this.getNode(node);
20604         if(typeof node.nodeIndex == "number"){
20605             return node.nodeIndex;
20606         }
20607         var ns = this.nodes;
20608         for(var i = 0, len = ns.length; i < len; i++){
20609             if(ns[i] == node){
20610                 return i;
20611             }
20612         }
20613         return -1;
20614     }
20615 });
20616 /*
20617  * - LGPL
20618  *
20619  * based on jquery fullcalendar
20620  * 
20621  */
20622
20623 Roo.bootstrap = Roo.bootstrap || {};
20624 /**
20625  * @class Roo.bootstrap.Calendar
20626  * @extends Roo.bootstrap.Component
20627  * Bootstrap Calendar class
20628  * @cfg {Boolean} loadMask (true|false) default false
20629  * @cfg {Object} header generate the user specific header of the calendar, default false
20630
20631  * @constructor
20632  * Create a new Container
20633  * @param {Object} config The config object
20634  */
20635
20636
20637
20638 Roo.bootstrap.Calendar = function(config){
20639     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20640      this.addEvents({
20641         /**
20642              * @event select
20643              * Fires when a date is selected
20644              * @param {DatePicker} this
20645              * @param {Date} date The selected date
20646              */
20647         'select': true,
20648         /**
20649              * @event monthchange
20650              * Fires when the displayed month changes 
20651              * @param {DatePicker} this
20652              * @param {Date} date The selected month
20653              */
20654         'monthchange': true,
20655         /**
20656              * @event evententer
20657              * Fires when mouse over an event
20658              * @param {Calendar} this
20659              * @param {event} Event
20660              */
20661         'evententer': true,
20662         /**
20663              * @event eventleave
20664              * Fires when the mouse leaves an
20665              * @param {Calendar} this
20666              * @param {event}
20667              */
20668         'eventleave': true,
20669         /**
20670              * @event eventclick
20671              * Fires when the mouse click an
20672              * @param {Calendar} this
20673              * @param {event}
20674              */
20675         'eventclick': true
20676         
20677     });
20678
20679 };
20680
20681 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20682     
20683           /**
20684      * @cfg {Roo.data.Store} store
20685      * The data source for the calendar
20686      */
20687         store : false,
20688      /**
20689      * @cfg {Number} startDay
20690      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20691      */
20692     startDay : 0,
20693     
20694     loadMask : false,
20695     
20696     header : false,
20697       
20698     getAutoCreate : function(){
20699         
20700         
20701         var fc_button = function(name, corner, style, content ) {
20702             return Roo.apply({},{
20703                 tag : 'span',
20704                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20705                          (corner.length ?
20706                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20707                             ''
20708                         ),
20709                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20710                 unselectable: 'on'
20711             });
20712         };
20713         
20714         var header = {};
20715         
20716         if(!this.header){
20717             header = {
20718                 tag : 'table',
20719                 cls : 'fc-header',
20720                 style : 'width:100%',
20721                 cn : [
20722                     {
20723                         tag: 'tr',
20724                         cn : [
20725                             {
20726                                 tag : 'td',
20727                                 cls : 'fc-header-left',
20728                                 cn : [
20729                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20730                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20731                                     { tag: 'span', cls: 'fc-header-space' },
20732                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20733
20734
20735                                 ]
20736                             },
20737
20738                             {
20739                                 tag : 'td',
20740                                 cls : 'fc-header-center',
20741                                 cn : [
20742                                     {
20743                                         tag: 'span',
20744                                         cls: 'fc-header-title',
20745                                         cn : {
20746                                             tag: 'H2',
20747                                             html : 'month / year'
20748                                         }
20749                                     }
20750
20751                                 ]
20752                             },
20753                             {
20754                                 tag : 'td',
20755                                 cls : 'fc-header-right',
20756                                 cn : [
20757                               /*      fc_button('month', 'left', '', 'month' ),
20758                                     fc_button('week', '', '', 'week' ),
20759                                     fc_button('day', 'right', '', 'day' )
20760                                 */    
20761
20762                                 ]
20763                             }
20764
20765                         ]
20766                     }
20767                 ]
20768             };
20769         }
20770         
20771         header = this.header;
20772         
20773        
20774         var cal_heads = function() {
20775             var ret = [];
20776             // fixme - handle this.
20777             
20778             for (var i =0; i < Date.dayNames.length; i++) {
20779                 var d = Date.dayNames[i];
20780                 ret.push({
20781                     tag: 'th',
20782                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20783                     html : d.substring(0,3)
20784                 });
20785                 
20786             }
20787             ret[0].cls += ' fc-first';
20788             ret[6].cls += ' fc-last';
20789             return ret;
20790         };
20791         var cal_cell = function(n) {
20792             return  {
20793                 tag: 'td',
20794                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20795                 cn : [
20796                     {
20797                         cn : [
20798                             {
20799                                 cls: 'fc-day-number',
20800                                 html: 'D'
20801                             },
20802                             {
20803                                 cls: 'fc-day-content',
20804                              
20805                                 cn : [
20806                                      {
20807                                         style: 'position: relative;' // height: 17px;
20808                                     }
20809                                 ]
20810                             }
20811                             
20812                             
20813                         ]
20814                     }
20815                 ]
20816                 
20817             }
20818         };
20819         var cal_rows = function() {
20820             
20821             var ret = [];
20822             for (var r = 0; r < 6; r++) {
20823                 var row= {
20824                     tag : 'tr',
20825                     cls : 'fc-week',
20826                     cn : []
20827                 };
20828                 
20829                 for (var i =0; i < Date.dayNames.length; i++) {
20830                     var d = Date.dayNames[i];
20831                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20832
20833                 }
20834                 row.cn[0].cls+=' fc-first';
20835                 row.cn[0].cn[0].style = 'min-height:90px';
20836                 row.cn[6].cls+=' fc-last';
20837                 ret.push(row);
20838                 
20839             }
20840             ret[0].cls += ' fc-first';
20841             ret[4].cls += ' fc-prev-last';
20842             ret[5].cls += ' fc-last';
20843             return ret;
20844             
20845         };
20846         
20847         var cal_table = {
20848             tag: 'table',
20849             cls: 'fc-border-separate',
20850             style : 'width:100%',
20851             cellspacing  : 0,
20852             cn : [
20853                 { 
20854                     tag: 'thead',
20855                     cn : [
20856                         { 
20857                             tag: 'tr',
20858                             cls : 'fc-first fc-last',
20859                             cn : cal_heads()
20860                         }
20861                     ]
20862                 },
20863                 { 
20864                     tag: 'tbody',
20865                     cn : cal_rows()
20866                 }
20867                   
20868             ]
20869         };
20870          
20871          var cfg = {
20872             cls : 'fc fc-ltr',
20873             cn : [
20874                 header,
20875                 {
20876                     cls : 'fc-content',
20877                     style : "position: relative;",
20878                     cn : [
20879                         {
20880                             cls : 'fc-view fc-view-month fc-grid',
20881                             style : 'position: relative',
20882                             unselectable : 'on',
20883                             cn : [
20884                                 {
20885                                     cls : 'fc-event-container',
20886                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20887                                 },
20888                                 cal_table
20889                             ]
20890                         }
20891                     ]
20892     
20893                 }
20894            ] 
20895             
20896         };
20897         
20898          
20899         
20900         return cfg;
20901     },
20902     
20903     
20904     initEvents : function()
20905     {
20906         if(!this.store){
20907             throw "can not find store for calendar";
20908         }
20909         
20910         var mark = {
20911             tag: "div",
20912             cls:"x-dlg-mask",
20913             style: "text-align:center",
20914             cn: [
20915                 {
20916                     tag: "div",
20917                     style: "background-color:white;width:50%;margin:250 auto",
20918                     cn: [
20919                         {
20920                             tag: "img",
20921                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20922                         },
20923                         {
20924                             tag: "span",
20925                             html: "Loading"
20926                         }
20927                         
20928                     ]
20929                 }
20930             ]
20931         };
20932         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20933         
20934         var size = this.el.select('.fc-content', true).first().getSize();
20935         this.maskEl.setSize(size.width, size.height);
20936         this.maskEl.enableDisplayMode("block");
20937         if(!this.loadMask){
20938             this.maskEl.hide();
20939         }
20940         
20941         this.store = Roo.factory(this.store, Roo.data);
20942         this.store.on('load', this.onLoad, this);
20943         this.store.on('beforeload', this.onBeforeLoad, this);
20944         
20945         this.resize();
20946         
20947         this.cells = this.el.select('.fc-day',true);
20948         //Roo.log(this.cells);
20949         this.textNodes = this.el.query('.fc-day-number');
20950         this.cells.addClassOnOver('fc-state-hover');
20951         
20952         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20953         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20954         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20955         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20956         
20957         this.on('monthchange', this.onMonthChange, this);
20958         
20959         this.update(new Date().clearTime());
20960     },
20961     
20962     resize : function() {
20963         var sz  = this.el.getSize();
20964         
20965         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20966         this.el.select('.fc-day-content div',true).setHeight(34);
20967     },
20968     
20969     
20970     // private
20971     showPrevMonth : function(e){
20972         this.update(this.activeDate.add("mo", -1));
20973     },
20974     showToday : function(e){
20975         this.update(new Date().clearTime());
20976     },
20977     // private
20978     showNextMonth : function(e){
20979         this.update(this.activeDate.add("mo", 1));
20980     },
20981
20982     // private
20983     showPrevYear : function(){
20984         this.update(this.activeDate.add("y", -1));
20985     },
20986
20987     // private
20988     showNextYear : function(){
20989         this.update(this.activeDate.add("y", 1));
20990     },
20991
20992     
20993    // private
20994     update : function(date)
20995     {
20996         var vd = this.activeDate;
20997         this.activeDate = date;
20998 //        if(vd && this.el){
20999 //            var t = date.getTime();
21000 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21001 //                Roo.log('using add remove');
21002 //                
21003 //                this.fireEvent('monthchange', this, date);
21004 //                
21005 //                this.cells.removeClass("fc-state-highlight");
21006 //                this.cells.each(function(c){
21007 //                   if(c.dateValue == t){
21008 //                       c.addClass("fc-state-highlight");
21009 //                       setTimeout(function(){
21010 //                            try{c.dom.firstChild.focus();}catch(e){}
21011 //                       }, 50);
21012 //                       return false;
21013 //                   }
21014 //                   return true;
21015 //                });
21016 //                return;
21017 //            }
21018 //        }
21019         
21020         var days = date.getDaysInMonth();
21021         
21022         var firstOfMonth = date.getFirstDateOfMonth();
21023         var startingPos = firstOfMonth.getDay()-this.startDay;
21024         
21025         if(startingPos < this.startDay){
21026             startingPos += 7;
21027         }
21028         
21029         var pm = date.add(Date.MONTH, -1);
21030         var prevStart = pm.getDaysInMonth()-startingPos;
21031 //        
21032         this.cells = this.el.select('.fc-day',true);
21033         this.textNodes = this.el.query('.fc-day-number');
21034         this.cells.addClassOnOver('fc-state-hover');
21035         
21036         var cells = this.cells.elements;
21037         var textEls = this.textNodes;
21038         
21039         Roo.each(cells, function(cell){
21040             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21041         });
21042         
21043         days += startingPos;
21044
21045         // convert everything to numbers so it's fast
21046         var day = 86400000;
21047         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21048         //Roo.log(d);
21049         //Roo.log(pm);
21050         //Roo.log(prevStart);
21051         
21052         var today = new Date().clearTime().getTime();
21053         var sel = date.clearTime().getTime();
21054         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21055         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21056         var ddMatch = this.disabledDatesRE;
21057         var ddText = this.disabledDatesText;
21058         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21059         var ddaysText = this.disabledDaysText;
21060         var format = this.format;
21061         
21062         var setCellClass = function(cal, cell){
21063             cell.row = 0;
21064             cell.events = [];
21065             cell.more = [];
21066             //Roo.log('set Cell Class');
21067             cell.title = "";
21068             var t = d.getTime();
21069             
21070             //Roo.log(d);
21071             
21072             cell.dateValue = t;
21073             if(t == today){
21074                 cell.className += " fc-today";
21075                 cell.className += " fc-state-highlight";
21076                 cell.title = cal.todayText;
21077             }
21078             if(t == sel){
21079                 // disable highlight in other month..
21080                 //cell.className += " fc-state-highlight";
21081                 
21082             }
21083             // disabling
21084             if(t < min) {
21085                 cell.className = " fc-state-disabled";
21086                 cell.title = cal.minText;
21087                 return;
21088             }
21089             if(t > max) {
21090                 cell.className = " fc-state-disabled";
21091                 cell.title = cal.maxText;
21092                 return;
21093             }
21094             if(ddays){
21095                 if(ddays.indexOf(d.getDay()) != -1){
21096                     cell.title = ddaysText;
21097                     cell.className = " fc-state-disabled";
21098                 }
21099             }
21100             if(ddMatch && format){
21101                 var fvalue = d.dateFormat(format);
21102                 if(ddMatch.test(fvalue)){
21103                     cell.title = ddText.replace("%0", fvalue);
21104                     cell.className = " fc-state-disabled";
21105                 }
21106             }
21107             
21108             if (!cell.initialClassName) {
21109                 cell.initialClassName = cell.dom.className;
21110             }
21111             
21112             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21113         };
21114
21115         var i = 0;
21116         
21117         for(; i < startingPos; i++) {
21118             textEls[i].innerHTML = (++prevStart);
21119             d.setDate(d.getDate()+1);
21120             
21121             cells[i].className = "fc-past fc-other-month";
21122             setCellClass(this, cells[i]);
21123         }
21124         
21125         var intDay = 0;
21126         
21127         for(; i < days; i++){
21128             intDay = i - startingPos + 1;
21129             textEls[i].innerHTML = (intDay);
21130             d.setDate(d.getDate()+1);
21131             
21132             cells[i].className = ''; // "x-date-active";
21133             setCellClass(this, cells[i]);
21134         }
21135         var extraDays = 0;
21136         
21137         for(; i < 42; i++) {
21138             textEls[i].innerHTML = (++extraDays);
21139             d.setDate(d.getDate()+1);
21140             
21141             cells[i].className = "fc-future fc-other-month";
21142             setCellClass(this, cells[i]);
21143         }
21144         
21145         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21146         
21147         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21148         
21149         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21150         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21151         
21152         if(totalRows != 6){
21153             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21154             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21155         }
21156         
21157         this.fireEvent('monthchange', this, date);
21158         
21159         
21160         /*
21161         if(!this.internalRender){
21162             var main = this.el.dom.firstChild;
21163             var w = main.offsetWidth;
21164             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21165             Roo.fly(main).setWidth(w);
21166             this.internalRender = true;
21167             // opera does not respect the auto grow header center column
21168             // then, after it gets a width opera refuses to recalculate
21169             // without a second pass
21170             if(Roo.isOpera && !this.secondPass){
21171                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21172                 this.secondPass = true;
21173                 this.update.defer(10, this, [date]);
21174             }
21175         }
21176         */
21177         
21178     },
21179     
21180     findCell : function(dt) {
21181         dt = dt.clearTime().getTime();
21182         var ret = false;
21183         this.cells.each(function(c){
21184             //Roo.log("check " +c.dateValue + '?=' + dt);
21185             if(c.dateValue == dt){
21186                 ret = c;
21187                 return false;
21188             }
21189             return true;
21190         });
21191         
21192         return ret;
21193     },
21194     
21195     findCells : function(ev) {
21196         var s = ev.start.clone().clearTime().getTime();
21197        // Roo.log(s);
21198         var e= ev.end.clone().clearTime().getTime();
21199        // Roo.log(e);
21200         var ret = [];
21201         this.cells.each(function(c){
21202              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21203             
21204             if(c.dateValue > e){
21205                 return ;
21206             }
21207             if(c.dateValue < s){
21208                 return ;
21209             }
21210             ret.push(c);
21211         });
21212         
21213         return ret;    
21214     },
21215     
21216 //    findBestRow: function(cells)
21217 //    {
21218 //        var ret = 0;
21219 //        
21220 //        for (var i =0 ; i < cells.length;i++) {
21221 //            ret  = Math.max(cells[i].rows || 0,ret);
21222 //        }
21223 //        return ret;
21224 //        
21225 //    },
21226     
21227     
21228     addItem : function(ev)
21229     {
21230         // look for vertical location slot in
21231         var cells = this.findCells(ev);
21232         
21233 //        ev.row = this.findBestRow(cells);
21234         
21235         // work out the location.
21236         
21237         var crow = false;
21238         var rows = [];
21239         for(var i =0; i < cells.length; i++) {
21240             
21241             cells[i].row = cells[0].row;
21242             
21243             if(i == 0){
21244                 cells[i].row = cells[i].row + 1;
21245             }
21246             
21247             if (!crow) {
21248                 crow = {
21249                     start : cells[i],
21250                     end :  cells[i]
21251                 };
21252                 continue;
21253             }
21254             if (crow.start.getY() == cells[i].getY()) {
21255                 // on same row.
21256                 crow.end = cells[i];
21257                 continue;
21258             }
21259             // different row.
21260             rows.push(crow);
21261             crow = {
21262                 start: cells[i],
21263                 end : cells[i]
21264             };
21265             
21266         }
21267         
21268         rows.push(crow);
21269         ev.els = [];
21270         ev.rows = rows;
21271         ev.cells = cells;
21272         
21273         cells[0].events.push(ev);
21274         
21275         this.calevents.push(ev);
21276     },
21277     
21278     clearEvents: function() {
21279         
21280         if(!this.calevents){
21281             return;
21282         }
21283         
21284         Roo.each(this.cells.elements, function(c){
21285             c.row = 0;
21286             c.events = [];
21287             c.more = [];
21288         });
21289         
21290         Roo.each(this.calevents, function(e) {
21291             Roo.each(e.els, function(el) {
21292                 el.un('mouseenter' ,this.onEventEnter, this);
21293                 el.un('mouseleave' ,this.onEventLeave, this);
21294                 el.remove();
21295             },this);
21296         },this);
21297         
21298         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21299             e.remove();
21300         });
21301         
21302     },
21303     
21304     renderEvents: function()
21305     {   
21306         var _this = this;
21307         
21308         this.cells.each(function(c) {
21309             
21310             if(c.row < 5){
21311                 return;
21312             }
21313             
21314             var ev = c.events;
21315             
21316             var r = 4;
21317             if(c.row != c.events.length){
21318                 r = 4 - (4 - (c.row - c.events.length));
21319             }
21320             
21321             c.events = ev.slice(0, r);
21322             c.more = ev.slice(r);
21323             
21324             if(c.more.length && c.more.length == 1){
21325                 c.events.push(c.more.pop());
21326             }
21327             
21328             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21329             
21330         });
21331             
21332         this.cells.each(function(c) {
21333             
21334             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21335             
21336             
21337             for (var e = 0; e < c.events.length; e++){
21338                 var ev = c.events[e];
21339                 var rows = ev.rows;
21340                 
21341                 for(var i = 0; i < rows.length; i++) {
21342                 
21343                     // how many rows should it span..
21344
21345                     var  cfg = {
21346                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21347                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21348
21349                         unselectable : "on",
21350                         cn : [
21351                             {
21352                                 cls: 'fc-event-inner',
21353                                 cn : [
21354     //                                {
21355     //                                  tag:'span',
21356     //                                  cls: 'fc-event-time',
21357     //                                  html : cells.length > 1 ? '' : ev.time
21358     //                                },
21359                                     {
21360                                       tag:'span',
21361                                       cls: 'fc-event-title',
21362                                       html : String.format('{0}', ev.title)
21363                                     }
21364
21365
21366                                 ]
21367                             },
21368                             {
21369                                 cls: 'ui-resizable-handle ui-resizable-e',
21370                                 html : '&nbsp;&nbsp;&nbsp'
21371                             }
21372
21373                         ]
21374                     };
21375
21376                     if (i == 0) {
21377                         cfg.cls += ' fc-event-start';
21378                     }
21379                     if ((i+1) == rows.length) {
21380                         cfg.cls += ' fc-event-end';
21381                     }
21382
21383                     var ctr = _this.el.select('.fc-event-container',true).first();
21384                     var cg = ctr.createChild(cfg);
21385
21386                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21387                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21388
21389                     var r = (c.more.length) ? 1 : 0;
21390                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21391                     cg.setWidth(ebox.right - sbox.x -2);
21392
21393                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21394                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21395                     cg.on('click', _this.onEventClick, _this, ev);
21396
21397                     ev.els.push(cg);
21398                     
21399                 }
21400                 
21401             }
21402             
21403             
21404             if(c.more.length){
21405                 var  cfg = {
21406                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21407                     style : 'position: absolute',
21408                     unselectable : "on",
21409                     cn : [
21410                         {
21411                             cls: 'fc-event-inner',
21412                             cn : [
21413                                 {
21414                                   tag:'span',
21415                                   cls: 'fc-event-title',
21416                                   html : 'More'
21417                                 }
21418
21419
21420                             ]
21421                         },
21422                         {
21423                             cls: 'ui-resizable-handle ui-resizable-e',
21424                             html : '&nbsp;&nbsp;&nbsp'
21425                         }
21426
21427                     ]
21428                 };
21429
21430                 var ctr = _this.el.select('.fc-event-container',true).first();
21431                 var cg = ctr.createChild(cfg);
21432
21433                 var sbox = c.select('.fc-day-content',true).first().getBox();
21434                 var ebox = c.select('.fc-day-content',true).first().getBox();
21435                 //Roo.log(cg);
21436                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21437                 cg.setWidth(ebox.right - sbox.x -2);
21438
21439                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21440                 
21441             }
21442             
21443         });
21444         
21445         
21446         
21447     },
21448     
21449     onEventEnter: function (e, el,event,d) {
21450         this.fireEvent('evententer', this, el, event);
21451     },
21452     
21453     onEventLeave: function (e, el,event,d) {
21454         this.fireEvent('eventleave', this, el, event);
21455     },
21456     
21457     onEventClick: function (e, el,event,d) {
21458         this.fireEvent('eventclick', this, el, event);
21459     },
21460     
21461     onMonthChange: function () {
21462         this.store.load();
21463     },
21464     
21465     onMoreEventClick: function(e, el, more)
21466     {
21467         var _this = this;
21468         
21469         this.calpopover.placement = 'right';
21470         this.calpopover.setTitle('More');
21471         
21472         this.calpopover.setContent('');
21473         
21474         var ctr = this.calpopover.el.select('.popover-content', true).first();
21475         
21476         Roo.each(more, function(m){
21477             var cfg = {
21478                 cls : 'fc-event-hori fc-event-draggable',
21479                 html : m.title
21480             };
21481             var cg = ctr.createChild(cfg);
21482             
21483             cg.on('click', _this.onEventClick, _this, m);
21484         });
21485         
21486         this.calpopover.show(el);
21487         
21488         
21489     },
21490     
21491     onLoad: function () 
21492     {   
21493         this.calevents = [];
21494         var cal = this;
21495         
21496         if(this.store.getCount() > 0){
21497             this.store.data.each(function(d){
21498                cal.addItem({
21499                     id : d.data.id,
21500                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21501                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21502                     time : d.data.start_time,
21503                     title : d.data.title,
21504                     description : d.data.description,
21505                     venue : d.data.venue
21506                 });
21507             });
21508         }
21509         
21510         this.renderEvents();
21511         
21512         if(this.calevents.length && this.loadMask){
21513             this.maskEl.hide();
21514         }
21515     },
21516     
21517     onBeforeLoad: function()
21518     {
21519         this.clearEvents();
21520         if(this.loadMask){
21521             this.maskEl.show();
21522         }
21523     }
21524 });
21525
21526  
21527  /*
21528  * - LGPL
21529  *
21530  * element
21531  * 
21532  */
21533
21534 /**
21535  * @class Roo.bootstrap.Popover
21536  * @extends Roo.bootstrap.Component
21537  * @parent none builder
21538  * @children Roo.bootstrap.Component
21539  * Bootstrap Popover class
21540  * @cfg {String} html contents of the popover   (or false to use children..)
21541  * @cfg {String} title of popover (or false to hide)
21542  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21543  * @cfg {String} trigger click || hover (or false to trigger manually)
21544  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21545  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21546  *      - if false and it has a 'parent' then it will be automatically added to that element
21547  *      - if string - Roo.get  will be called 
21548  * @cfg {Number} delay - delay before showing
21549  
21550  * @constructor
21551  * Create a new Popover
21552  * @param {Object} config The config object
21553  */
21554
21555 Roo.bootstrap.Popover = function(config){
21556     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21557     
21558     this.addEvents({
21559         // raw events
21560          /**
21561          * @event show
21562          * After the popover show
21563          * 
21564          * @param {Roo.bootstrap.Popover} this
21565          */
21566         "show" : true,
21567         /**
21568          * @event hide
21569          * After the popover hide
21570          * 
21571          * @param {Roo.bootstrap.Popover} this
21572          */
21573         "hide" : true
21574     });
21575 };
21576
21577 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21578     
21579     title: false,
21580     html: false,
21581     
21582     placement : 'right',
21583     trigger : 'hover', // hover
21584     modal : false,
21585     delay : 0,
21586     
21587     over: false,
21588     
21589     can_build_overlaid : false,
21590     
21591     maskEl : false, // the mask element
21592     headerEl : false,
21593     contentEl : false,
21594     alignEl : false, // when show is called with an element - this get's stored.
21595     
21596     getChildContainer : function()
21597     {
21598         return this.contentEl;
21599         
21600     },
21601     getPopoverHeader : function()
21602     {
21603         this.title = true; // flag not to hide it..
21604         this.headerEl.addClass('p-0');
21605         return this.headerEl
21606     },
21607     
21608     
21609     getAutoCreate : function(){
21610          
21611         var cfg = {
21612            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21613            style: 'display:block',
21614            cn : [
21615                 {
21616                     cls : 'arrow'
21617                 },
21618                 {
21619                     cls : 'popover-inner ',
21620                     cn : [
21621                         {
21622                             tag: 'h3',
21623                             cls: 'popover-title popover-header',
21624                             html : this.title === false ? '' : this.title
21625                         },
21626                         {
21627                             cls : 'popover-content popover-body '  + (this.cls || ''),
21628                             html : this.html || ''
21629                         }
21630                     ]
21631                     
21632                 }
21633            ]
21634         };
21635         
21636         return cfg;
21637     },
21638     /**
21639      * @param {string} the title
21640      */
21641     setTitle: function(str)
21642     {
21643         this.title = str;
21644         if (this.el) {
21645             this.headerEl.dom.innerHTML = str;
21646         }
21647         
21648     },
21649     /**
21650      * @param {string} the body content
21651      */
21652     setContent: function(str)
21653     {
21654         this.html = str;
21655         if (this.contentEl) {
21656             this.contentEl.dom.innerHTML = str;
21657         }
21658         
21659     },
21660     // as it get's added to the bottom of the page.
21661     onRender : function(ct, position)
21662     {
21663         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21664         
21665         
21666         
21667         if(!this.el){
21668             var cfg = Roo.apply({},  this.getAutoCreate());
21669             cfg.id = Roo.id();
21670             
21671             if (this.cls) {
21672                 cfg.cls += ' ' + this.cls;
21673             }
21674             if (this.style) {
21675                 cfg.style = this.style;
21676             }
21677             //Roo.log("adding to ");
21678             this.el = Roo.get(document.body).createChild(cfg, position);
21679 //            Roo.log(this.el);
21680         }
21681         
21682         this.contentEl = this.el.select('.popover-content',true).first();
21683         this.headerEl =  this.el.select('.popover-title',true).first();
21684         
21685         var nitems = [];
21686         if(typeof(this.items) != 'undefined'){
21687             var items = this.items;
21688             delete this.items;
21689
21690             for(var i =0;i < items.length;i++) {
21691                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21692             }
21693         }
21694
21695         this.items = nitems;
21696         
21697         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21698         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21699         
21700         
21701         
21702         this.initEvents();
21703     },
21704     
21705     resizeMask : function()
21706     {
21707         this.maskEl.setSize(
21708             Roo.lib.Dom.getViewWidth(true),
21709             Roo.lib.Dom.getViewHeight(true)
21710         );
21711     },
21712     
21713     initEvents : function()
21714     {
21715         
21716         if (!this.modal) { 
21717             Roo.bootstrap.Popover.register(this);
21718         }
21719          
21720         this.arrowEl = this.el.select('.arrow',true).first();
21721         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21722         this.el.enableDisplayMode('block');
21723         this.el.hide();
21724  
21725         
21726         if (this.over === false && !this.parent()) {
21727             return; 
21728         }
21729         if (this.triggers === false) {
21730             return;
21731         }
21732          
21733         // support parent
21734         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21735         var triggers = this.trigger ? this.trigger.split(' ') : [];
21736         Roo.each(triggers, function(trigger) {
21737         
21738             if (trigger == 'click') {
21739                 on_el.on('click', this.toggle, this);
21740             } else if (trigger != 'manual') {
21741                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21742                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21743       
21744                 on_el.on(eventIn  ,this.enter, this);
21745                 on_el.on(eventOut, this.leave, this);
21746             }
21747         }, this);
21748     },
21749     
21750     
21751     // private
21752     timeout : null,
21753     hoverState : null,
21754     
21755     toggle : function () {
21756         this.hoverState == 'in' ? this.leave() : this.enter();
21757     },
21758     
21759     enter : function () {
21760         
21761         clearTimeout(this.timeout);
21762     
21763         this.hoverState = 'in';
21764     
21765         if (!this.delay || !this.delay.show) {
21766             this.show();
21767             return;
21768         }
21769         var _t = this;
21770         this.timeout = setTimeout(function () {
21771             if (_t.hoverState == 'in') {
21772                 _t.show();
21773             }
21774         }, this.delay.show)
21775     },
21776     
21777     leave : function() {
21778         clearTimeout(this.timeout);
21779     
21780         this.hoverState = 'out';
21781     
21782         if (!this.delay || !this.delay.hide) {
21783             this.hide();
21784             return;
21785         }
21786         var _t = this;
21787         this.timeout = setTimeout(function () {
21788             if (_t.hoverState == 'out') {
21789                 _t.hide();
21790             }
21791         }, this.delay.hide)
21792     },
21793     
21794     /**
21795      * update the position of the dialog
21796      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21797      * 
21798      *
21799      */
21800     
21801     doAlign : function()
21802     {
21803         
21804         if (this.alignEl) {
21805             this.updatePosition(this.placement, true);
21806              
21807         } else {
21808             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21809             var es = this.el.getSize();
21810             var x = Roo.lib.Dom.getViewWidth()/2;
21811             var y = Roo.lib.Dom.getViewHeight()/2;
21812             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21813             
21814         }
21815
21816          
21817          
21818         
21819         
21820     },
21821     
21822     /**
21823      * Show the popover
21824      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21825      * @param {string} (left|right|top|bottom) position
21826      */
21827     show : function (on_el, placement)
21828     {
21829         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21830         on_el = on_el || false; // default to false
21831          
21832         if (!on_el) {
21833             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21834                 on_el = this.parent().el;
21835             } else if (this.over) {
21836                 on_el = Roo.get(this.over);
21837             }
21838             
21839         }
21840         
21841         this.alignEl = Roo.get( on_el );
21842
21843         if (!this.el) {
21844             this.render(document.body);
21845         }
21846         
21847         
21848          
21849         
21850         if (this.title === false) {
21851             this.headerEl.hide();
21852         }
21853         
21854        
21855         this.el.show();
21856         this.el.dom.style.display = 'block';
21857          
21858         this.doAlign();
21859         
21860         //var arrow = this.el.select('.arrow',true).first();
21861         //arrow.set(align[2], 
21862         
21863         this.el.addClass('in');
21864         
21865          
21866         
21867         this.hoverState = 'in';
21868         
21869         if (this.modal) {
21870             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21871             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21872             this.maskEl.dom.style.display = 'block';
21873             this.maskEl.addClass('show');
21874         }
21875         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21876  
21877         this.fireEvent('show', this);
21878         
21879     },
21880     /**
21881      * fire this manually after loading a grid in the table for example
21882      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21883      * @param {Boolean} try and move it if we cant get right position.
21884      */
21885     updatePosition : function(placement, try_move)
21886     {
21887         // allow for calling with no parameters
21888         placement = placement   ? placement :  this.placement;
21889         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21890         
21891         this.el.removeClass([
21892             'fade','top','bottom', 'left', 'right','in',
21893             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21894         ]);
21895         this.el.addClass(placement + ' bs-popover-' + placement);
21896         
21897         if (!this.alignEl ) {
21898             return false;
21899         }
21900         
21901         switch (placement) {
21902             case 'right':
21903                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21904                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21905                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21906                     //normal display... or moved up/down.
21907                     this.el.setXY(offset);
21908                     var xy = this.alignEl.getAnchorXY('tr', false);
21909                     xy[0]+=2;xy[1]+=5;
21910                     this.arrowEl.setXY(xy);
21911                     return true;
21912                 }
21913                 // continue through...
21914                 return this.updatePosition('left', false);
21915                 
21916             
21917             case 'left':
21918                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21919                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21920                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21921                     //normal display... or moved up/down.
21922                     this.el.setXY(offset);
21923                     var xy = this.alignEl.getAnchorXY('tl', false);
21924                     xy[0]-=10;xy[1]+=5; // << fix me
21925                     this.arrowEl.setXY(xy);
21926                     return true;
21927                 }
21928                 // call self...
21929                 return this.updatePosition('right', false);
21930             
21931             case 'top':
21932                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21933                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21934                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21935                     //normal display... or moved up/down.
21936                     this.el.setXY(offset);
21937                     var xy = this.alignEl.getAnchorXY('t', false);
21938                     xy[1]-=10; // << fix me
21939                     this.arrowEl.setXY(xy);
21940                     return true;
21941                 }
21942                 // fall through
21943                return this.updatePosition('bottom', false);
21944             
21945             case 'bottom':
21946                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21947                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21948                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21949                     //normal display... or moved up/down.
21950                     this.el.setXY(offset);
21951                     var xy = this.alignEl.getAnchorXY('b', false);
21952                      xy[1]+=2; // << fix me
21953                     this.arrowEl.setXY(xy);
21954                     return true;
21955                 }
21956                 // fall through
21957                 return this.updatePosition('top', false);
21958                 
21959             
21960         }
21961         
21962         
21963         return false;
21964     },
21965     
21966     hide : function()
21967     {
21968         this.el.setXY([0,0]);
21969         this.el.removeClass('in');
21970         this.el.hide();
21971         this.hoverState = null;
21972         this.maskEl.hide(); // always..
21973         this.fireEvent('hide', this);
21974     }
21975     
21976 });
21977
21978
21979 Roo.apply(Roo.bootstrap.Popover, {
21980
21981     alignment : {
21982         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21983         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21984         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21985         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21986     },
21987     
21988     zIndex : 20001,
21989
21990     clickHander : false,
21991     
21992     
21993
21994     onMouseDown : function(e)
21995     {
21996         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21997             /// what is nothing is showing..
21998             this.hideAll();
21999         }
22000          
22001     },
22002     
22003     
22004     popups : [],
22005     
22006     register : function(popup)
22007     {
22008         if (!Roo.bootstrap.Popover.clickHandler) {
22009             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22010         }
22011         // hide other popups.
22012         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
22013         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
22014         this.hideAll(); //<< why?
22015         //this.popups.push(popup);
22016     },
22017     hideAll : function()
22018     {
22019         this.popups.forEach(function(p) {
22020             p.hide();
22021         });
22022     },
22023     onShow : function() {
22024         Roo.bootstrap.Popover.popups.push(this);
22025     },
22026     onHide : function() {
22027         Roo.bootstrap.Popover.popups.remove(this);
22028     } 
22029
22030 });
22031 /**
22032  * @class Roo.bootstrap.PopoverNav
22033  * @extends Roo.bootstrap.nav.Simplebar
22034  * @parent Roo.bootstrap.Popover
22035  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22036  * @licence LGPL
22037  * Bootstrap Popover header navigation class
22038  * FIXME? should this go under nav?
22039  *
22040  * 
22041  * @constructor
22042  * Create a new Popover Header Navigation 
22043  * @param {Object} config The config object
22044  */
22045
22046 Roo.bootstrap.PopoverNav = function(config){
22047     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22048 };
22049
22050 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22051     
22052     
22053     container_method : 'getPopoverHeader' 
22054     
22055      
22056     
22057     
22058    
22059 });
22060
22061  
22062
22063  /*
22064  * - LGPL
22065  *
22066  * Progress
22067  * 
22068  */
22069
22070 /**
22071  * @class Roo.bootstrap.Progress
22072  * @extends Roo.bootstrap.Component
22073  * @children Roo.bootstrap.ProgressBar
22074  * Bootstrap Progress class
22075  * @cfg {Boolean} striped striped of the progress bar
22076  * @cfg {Boolean} active animated of the progress bar
22077  * 
22078  * 
22079  * @constructor
22080  * Create a new Progress
22081  * @param {Object} config The config object
22082  */
22083
22084 Roo.bootstrap.Progress = function(config){
22085     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22086 };
22087
22088 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22089     
22090     striped : false,
22091     active: false,
22092     
22093     getAutoCreate : function(){
22094         var cfg = {
22095             tag: 'div',
22096             cls: 'progress'
22097         };
22098         
22099         
22100         if(this.striped){
22101             cfg.cls += ' progress-striped';
22102         }
22103       
22104         if(this.active){
22105             cfg.cls += ' active';
22106         }
22107         
22108         
22109         return cfg;
22110     }
22111    
22112 });
22113
22114  
22115
22116  /*
22117  * - LGPL
22118  *
22119  * ProgressBar
22120  * 
22121  */
22122
22123 /**
22124  * @class Roo.bootstrap.ProgressBar
22125  * @extends Roo.bootstrap.Component
22126  * Bootstrap ProgressBar class
22127  * @cfg {Number} aria_valuenow aria-value now
22128  * @cfg {Number} aria_valuemin aria-value min
22129  * @cfg {Number} aria_valuemax aria-value max
22130  * @cfg {String} label label for the progress bar
22131  * @cfg {String} panel (success | info | warning | danger )
22132  * @cfg {String} role role of the progress bar
22133  * @cfg {String} sr_only text
22134  * 
22135  * 
22136  * @constructor
22137  * Create a new ProgressBar
22138  * @param {Object} config The config object
22139  */
22140
22141 Roo.bootstrap.ProgressBar = function(config){
22142     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22143 };
22144
22145 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22146     
22147     aria_valuenow : 0,
22148     aria_valuemin : 0,
22149     aria_valuemax : 100,
22150     label : false,
22151     panel : false,
22152     role : false,
22153     sr_only: false,
22154     
22155     getAutoCreate : function()
22156     {
22157         
22158         var cfg = {
22159             tag: 'div',
22160             cls: 'progress-bar',
22161             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22162         };
22163         
22164         if(this.sr_only){
22165             cfg.cn = {
22166                 tag: 'span',
22167                 cls: 'sr-only',
22168                 html: this.sr_only
22169             }
22170         }
22171         
22172         if(this.role){
22173             cfg.role = this.role;
22174         }
22175         
22176         if(this.aria_valuenow){
22177             cfg['aria-valuenow'] = this.aria_valuenow;
22178         }
22179         
22180         if(this.aria_valuemin){
22181             cfg['aria-valuemin'] = this.aria_valuemin;
22182         }
22183         
22184         if(this.aria_valuemax){
22185             cfg['aria-valuemax'] = this.aria_valuemax;
22186         }
22187         
22188         if(this.label && !this.sr_only){
22189             cfg.html = this.label;
22190         }
22191         
22192         if(this.panel){
22193             cfg.cls += ' progress-bar-' + this.panel;
22194         }
22195         
22196         return cfg;
22197     },
22198     
22199     update : function(aria_valuenow)
22200     {
22201         this.aria_valuenow = aria_valuenow;
22202         
22203         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22204     }
22205    
22206 });
22207
22208  
22209
22210  /**
22211  * @class Roo.bootstrap.TabGroup
22212  * @extends Roo.bootstrap.Column
22213  * @children Roo.bootstrap.TabPanel
22214  * Bootstrap Column class
22215  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22216  * @cfg {Boolean} carousel true to make the group behave like a carousel
22217  * @cfg {Boolean} bullets show bullets for the panels
22218  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22219  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22220  * @cfg {Boolean} showarrow (true|false) show arrow default true
22221  * 
22222  * @constructor
22223  * Create a new TabGroup
22224  * @param {Object} config The config object
22225  */
22226
22227 Roo.bootstrap.TabGroup = function(config){
22228     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22229     if (!this.navId) {
22230         this.navId = Roo.id();
22231     }
22232     this.tabs = [];
22233     Roo.bootstrap.TabGroup.register(this);
22234     
22235 };
22236
22237 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22238     
22239     carousel : false,
22240     transition : false,
22241     bullets : 0,
22242     timer : 0,
22243     autoslide : false,
22244     slideFn : false,
22245     slideOnTouch : false,
22246     showarrow : true,
22247     
22248     getAutoCreate : function()
22249     {
22250         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22251         
22252         cfg.cls += ' tab-content';
22253         
22254         if (this.carousel) {
22255             cfg.cls += ' carousel slide';
22256             
22257             cfg.cn = [{
22258                cls : 'carousel-inner',
22259                cn : []
22260             }];
22261         
22262             if(this.bullets  && !Roo.isTouch){
22263                 
22264                 var bullets = {
22265                     cls : 'carousel-bullets',
22266                     cn : []
22267                 };
22268                
22269                 if(this.bullets_cls){
22270                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22271                 }
22272                 
22273                 bullets.cn.push({
22274                     cls : 'clear'
22275                 });
22276                 
22277                 cfg.cn[0].cn.push(bullets);
22278             }
22279             
22280             if(this.showarrow){
22281                 cfg.cn[0].cn.push({
22282                     tag : 'div',
22283                     class : 'carousel-arrow',
22284                     cn : [
22285                         {
22286                             tag : 'div',
22287                             class : 'carousel-prev',
22288                             cn : [
22289                                 {
22290                                     tag : 'i',
22291                                     class : 'fa fa-chevron-left'
22292                                 }
22293                             ]
22294                         },
22295                         {
22296                             tag : 'div',
22297                             class : 'carousel-next',
22298                             cn : [
22299                                 {
22300                                     tag : 'i',
22301                                     class : 'fa fa-chevron-right'
22302                                 }
22303                             ]
22304                         }
22305                     ]
22306                 });
22307             }
22308             
22309         }
22310         
22311         return cfg;
22312     },
22313     
22314     initEvents:  function()
22315     {
22316 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22317 //            this.el.on("touchstart", this.onTouchStart, this);
22318 //        }
22319         
22320         if(this.autoslide){
22321             var _this = this;
22322             
22323             this.slideFn = window.setInterval(function() {
22324                 _this.showPanelNext();
22325             }, this.timer);
22326         }
22327         
22328         if(this.showarrow){
22329             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22330             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22331         }
22332         
22333         
22334     },
22335     
22336 //    onTouchStart : function(e, el, o)
22337 //    {
22338 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22339 //            return;
22340 //        }
22341 //        
22342 //        this.showPanelNext();
22343 //    },
22344     
22345     
22346     getChildContainer : function()
22347     {
22348         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22349     },
22350     
22351     /**
22352     * register a Navigation item
22353     * @param {Roo.bootstrap.nav.Item} the navitem to add
22354     */
22355     register : function(item)
22356     {
22357         this.tabs.push( item);
22358         item.navId = this.navId; // not really needed..
22359         this.addBullet();
22360     
22361     },
22362     
22363     getActivePanel : function()
22364     {
22365         var r = false;
22366         Roo.each(this.tabs, function(t) {
22367             if (t.active) {
22368                 r = t;
22369                 return false;
22370             }
22371             return null;
22372         });
22373         return r;
22374         
22375     },
22376     getPanelByName : function(n)
22377     {
22378         var r = false;
22379         Roo.each(this.tabs, function(t) {
22380             if (t.tabId == n) {
22381                 r = t;
22382                 return false;
22383             }
22384             return null;
22385         });
22386         return r;
22387     },
22388     indexOfPanel : function(p)
22389     {
22390         var r = false;
22391         Roo.each(this.tabs, function(t,i) {
22392             if (t.tabId == p.tabId) {
22393                 r = i;
22394                 return false;
22395             }
22396             return null;
22397         });
22398         return r;
22399     },
22400     /**
22401      * show a specific panel
22402      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22403      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22404      */
22405     showPanel : function (pan)
22406     {
22407         if(this.transition || typeof(pan) == 'undefined'){
22408             Roo.log("waiting for the transitionend");
22409             return false;
22410         }
22411         
22412         if (typeof(pan) == 'number') {
22413             pan = this.tabs[pan];
22414         }
22415         
22416         if (typeof(pan) == 'string') {
22417             pan = this.getPanelByName(pan);
22418         }
22419         
22420         var cur = this.getActivePanel();
22421         
22422         if(!pan || !cur){
22423             Roo.log('pan or acitve pan is undefined');
22424             return false;
22425         }
22426         
22427         if (pan.tabId == this.getActivePanel().tabId) {
22428             return true;
22429         }
22430         
22431         if (false === cur.fireEvent('beforedeactivate')) {
22432             return false;
22433         }
22434         
22435         if(this.bullets > 0 && !Roo.isTouch){
22436             this.setActiveBullet(this.indexOfPanel(pan));
22437         }
22438         
22439         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22440             
22441             //class="carousel-item carousel-item-next carousel-item-left"
22442             
22443             this.transition = true;
22444             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22445             var lr = dir == 'next' ? 'left' : 'right';
22446             pan.el.addClass(dir); // or prev
22447             pan.el.addClass('carousel-item-' + dir); // or prev
22448             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22449             cur.el.addClass(lr); // or right
22450             pan.el.addClass(lr);
22451             cur.el.addClass('carousel-item-' +lr); // or right
22452             pan.el.addClass('carousel-item-' +lr);
22453             
22454             
22455             var _this = this;
22456             cur.el.on('transitionend', function() {
22457                 Roo.log("trans end?");
22458                 
22459                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22460                 pan.setActive(true);
22461                 
22462                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22463                 cur.setActive(false);
22464                 
22465                 _this.transition = false;
22466                 
22467             }, this, { single:  true } );
22468             
22469             return true;
22470         }
22471         
22472         cur.setActive(false);
22473         pan.setActive(true);
22474         
22475         return true;
22476         
22477     },
22478     showPanelNext : function()
22479     {
22480         var i = this.indexOfPanel(this.getActivePanel());
22481         
22482         if (i >= this.tabs.length - 1 && !this.autoslide) {
22483             return;
22484         }
22485         
22486         if (i >= this.tabs.length - 1 && this.autoslide) {
22487             i = -1;
22488         }
22489         
22490         this.showPanel(this.tabs[i+1]);
22491     },
22492     
22493     showPanelPrev : function()
22494     {
22495         var i = this.indexOfPanel(this.getActivePanel());
22496         
22497         if (i  < 1 && !this.autoslide) {
22498             return;
22499         }
22500         
22501         if (i < 1 && this.autoslide) {
22502             i = this.tabs.length;
22503         }
22504         
22505         this.showPanel(this.tabs[i-1]);
22506     },
22507     
22508     
22509     addBullet: function()
22510     {
22511         if(!this.bullets || Roo.isTouch){
22512             return;
22513         }
22514         var ctr = this.el.select('.carousel-bullets',true).first();
22515         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22516         var bullet = ctr.createChild({
22517             cls : 'bullet bullet-' + i
22518         },ctr.dom.lastChild);
22519         
22520         
22521         var _this = this;
22522         
22523         bullet.on('click', (function(e, el, o, ii, t){
22524
22525             e.preventDefault();
22526
22527             this.showPanel(ii);
22528
22529             if(this.autoslide && this.slideFn){
22530                 clearInterval(this.slideFn);
22531                 this.slideFn = window.setInterval(function() {
22532                     _this.showPanelNext();
22533                 }, this.timer);
22534             }
22535
22536         }).createDelegate(this, [i, bullet], true));
22537                 
22538         
22539     },
22540      
22541     setActiveBullet : function(i)
22542     {
22543         if(Roo.isTouch){
22544             return;
22545         }
22546         
22547         Roo.each(this.el.select('.bullet', true).elements, function(el){
22548             el.removeClass('selected');
22549         });
22550
22551         var bullet = this.el.select('.bullet-' + i, true).first();
22552         
22553         if(!bullet){
22554             return;
22555         }
22556         
22557         bullet.addClass('selected');
22558     }
22559     
22560     
22561   
22562 });
22563
22564  
22565
22566  
22567  
22568 Roo.apply(Roo.bootstrap.TabGroup, {
22569     
22570     groups: {},
22571      /**
22572     * register a Navigation Group
22573     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22574     */
22575     register : function(navgrp)
22576     {
22577         this.groups[navgrp.navId] = navgrp;
22578         
22579     },
22580     /**
22581     * fetch a Navigation Group based on the navigation ID
22582     * if one does not exist , it will get created.
22583     * @param {string} the navgroup to add
22584     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22585     */
22586     get: function(navId) {
22587         if (typeof(this.groups[navId]) == 'undefined') {
22588             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22589         }
22590         return this.groups[navId] ;
22591     }
22592     
22593     
22594     
22595 });
22596
22597  /*
22598  * - LGPL
22599  *
22600  * TabPanel
22601  * 
22602  */
22603
22604 /**
22605  * @class Roo.bootstrap.TabPanel
22606  * @extends Roo.bootstrap.Component
22607  * @children Roo.bootstrap.Component
22608  * Bootstrap TabPanel class
22609  * @cfg {Boolean} active panel active
22610  * @cfg {String} html panel content
22611  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22612  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22613  * @cfg {String} href click to link..
22614  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22615  * 
22616  * 
22617  * @constructor
22618  * Create a new TabPanel
22619  * @param {Object} config The config object
22620  */
22621
22622 Roo.bootstrap.TabPanel = function(config){
22623     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22624     this.addEvents({
22625         /**
22626              * @event changed
22627              * Fires when the active status changes
22628              * @param {Roo.bootstrap.TabPanel} this
22629              * @param {Boolean} state the new state
22630             
22631          */
22632         'changed': true,
22633         /**
22634              * @event beforedeactivate
22635              * Fires before a tab is de-activated - can be used to do validation on a form.
22636              * @param {Roo.bootstrap.TabPanel} this
22637              * @return {Boolean} false if there is an error
22638             
22639          */
22640         'beforedeactivate': true
22641      });
22642     
22643     this.tabId = this.tabId || Roo.id();
22644   
22645 };
22646
22647 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22648     
22649     active: false,
22650     html: false,
22651     tabId: false,
22652     navId : false,
22653     href : '',
22654     touchSlide : false,
22655     getAutoCreate : function(){
22656         
22657         
22658         var cfg = {
22659             tag: 'div',
22660             // item is needed for carousel - not sure if it has any effect otherwise
22661             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22662             html: this.html || ''
22663         };
22664         
22665         if(this.active){
22666             cfg.cls += ' active';
22667         }
22668         
22669         if(this.tabId){
22670             cfg.tabId = this.tabId;
22671         }
22672         
22673         
22674         
22675         return cfg;
22676     },
22677     
22678     initEvents:  function()
22679     {
22680         var p = this.parent();
22681         
22682         this.navId = this.navId || p.navId;
22683         
22684         if (typeof(this.navId) != 'undefined') {
22685             // not really needed.. but just in case.. parent should be a NavGroup.
22686             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22687             
22688             tg.register(this);
22689             
22690             var i = tg.tabs.length - 1;
22691             
22692             if(this.active && tg.bullets > 0 && i < tg.bullets){
22693                 tg.setActiveBullet(i);
22694             }
22695         }
22696         
22697         this.el.on('click', this.onClick, this);
22698         
22699         if(Roo.isTouch && this.touchSlide){
22700             this.el.on("touchstart", this.onTouchStart, this);
22701             this.el.on("touchmove", this.onTouchMove, this);
22702             this.el.on("touchend", this.onTouchEnd, this);
22703         }
22704         
22705     },
22706     
22707     onRender : function(ct, position)
22708     {
22709         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22710     },
22711     
22712     setActive : function(state)
22713     {
22714         Roo.log("panel - set active " + this.tabId + "=" + state);
22715         
22716         this.active = state;
22717         if (!state) {
22718             this.el.removeClass('active');
22719             
22720         } else  if (!this.el.hasClass('active')) {
22721             this.el.addClass('active');
22722         }
22723         
22724         this.fireEvent('changed', this, state);
22725     },
22726     
22727     onClick : function(e)
22728     {
22729         e.preventDefault();
22730         
22731         if(!this.href.length){
22732             return;
22733         }
22734         
22735         window.location.href = this.href;
22736     },
22737     
22738     startX : 0,
22739     startY : 0,
22740     endX : 0,
22741     endY : 0,
22742     swiping : false,
22743     
22744     onTouchStart : function(e)
22745     {
22746         this.swiping = false;
22747         
22748         this.startX = e.browserEvent.touches[0].clientX;
22749         this.startY = e.browserEvent.touches[0].clientY;
22750     },
22751     
22752     onTouchMove : function(e)
22753     {
22754         this.swiping = true;
22755         
22756         this.endX = e.browserEvent.touches[0].clientX;
22757         this.endY = e.browserEvent.touches[0].clientY;
22758     },
22759     
22760     onTouchEnd : function(e)
22761     {
22762         if(!this.swiping){
22763             this.onClick(e);
22764             return;
22765         }
22766         
22767         var tabGroup = this.parent();
22768         
22769         if(this.endX > this.startX){ // swiping right
22770             tabGroup.showPanelPrev();
22771             return;
22772         }
22773         
22774         if(this.startX > this.endX){ // swiping left
22775             tabGroup.showPanelNext();
22776             return;
22777         }
22778     }
22779     
22780     
22781 });
22782  
22783
22784  
22785
22786  /*
22787  * - LGPL
22788  *
22789  * DateField
22790  * 
22791  */
22792
22793 /**
22794  * @class Roo.bootstrap.form.DateField
22795  * @extends Roo.bootstrap.form.Input
22796  * Bootstrap DateField class
22797  * @cfg {Number} weekStart default 0
22798  * @cfg {String} viewMode default empty, (months|years)
22799  * @cfg {String} minViewMode default empty, (months|years)
22800  * @cfg {Number} startDate default -Infinity
22801  * @cfg {Number} endDate default Infinity
22802  * @cfg {Boolean} todayHighlight default false
22803  * @cfg {Boolean} todayBtn default false
22804  * @cfg {Boolean} calendarWeeks default false
22805  * @cfg {Object} daysOfWeekDisabled default empty
22806  * @cfg {Boolean} singleMode default false (true | false)
22807  * 
22808  * @cfg {Boolean} keyboardNavigation default true
22809  * @cfg {String} language default en
22810  * 
22811  * @constructor
22812  * Create a new DateField
22813  * @param {Object} config The config object
22814  */
22815
22816 Roo.bootstrap.form.DateField = function(config){
22817     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22818      this.addEvents({
22819             /**
22820              * @event show
22821              * Fires when this field show.
22822              * @param {Roo.bootstrap.form.DateField} this
22823              * @param {Mixed} date The date value
22824              */
22825             show : true,
22826             /**
22827              * @event show
22828              * Fires when this field hide.
22829              * @param {Roo.bootstrap.form.DateField} this
22830              * @param {Mixed} date The date value
22831              */
22832             hide : true,
22833             /**
22834              * @event select
22835              * Fires when select a date.
22836              * @param {Roo.bootstrap.form.DateField} this
22837              * @param {Mixed} date The date value
22838              */
22839             select : true,
22840             /**
22841              * @event beforeselect
22842              * Fires when before select a date.
22843              * @param {Roo.bootstrap.form.DateField} this
22844              * @param {Mixed} date The date value
22845              */
22846             beforeselect : true
22847         });
22848 };
22849
22850 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22851     
22852     /**
22853      * @cfg {String} format
22854      * The default date format string which can be overriden for localization support.  The format must be
22855      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22856      */
22857     format : "m/d/y",
22858     /**
22859      * @cfg {String} altFormats
22860      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22861      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22862      */
22863     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22864     
22865     weekStart : 0,
22866     
22867     viewMode : '',
22868     
22869     minViewMode : '',
22870     
22871     todayHighlight : false,
22872     
22873     todayBtn: false,
22874     
22875     language: 'en',
22876     
22877     keyboardNavigation: true,
22878     
22879     calendarWeeks: false,
22880     
22881     startDate: -Infinity,
22882     
22883     endDate: Infinity,
22884     
22885     daysOfWeekDisabled: [],
22886     
22887     _events: [],
22888     
22889     singleMode : false,
22890     
22891     UTCDate: function()
22892     {
22893         return new Date(Date.UTC.apply(Date, arguments));
22894     },
22895     
22896     UTCToday: function()
22897     {
22898         var today = new Date();
22899         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22900     },
22901     
22902     getDate: function() {
22903             var d = this.getUTCDate();
22904             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22905     },
22906     
22907     getUTCDate: function() {
22908             return this.date;
22909     },
22910     
22911     setDate: function(d) {
22912             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22913     },
22914     
22915     setUTCDate: function(d) {
22916             this.date = d;
22917             this.setValue(this.formatDate(this.date));
22918     },
22919         
22920     onRender: function(ct, position)
22921     {
22922         
22923         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22924         
22925         this.language = this.language || 'en';
22926         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22927         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22928         
22929         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22930         this.format = this.format || 'm/d/y';
22931         this.isInline = false;
22932         this.isInput = true;
22933         this.component = this.el.select('.add-on', true).first() || false;
22934         this.component = (this.component && this.component.length === 0) ? false : this.component;
22935         this.hasInput = this.component && this.inputEl().length;
22936         
22937         if (typeof(this.minViewMode === 'string')) {
22938             switch (this.minViewMode) {
22939                 case 'months':
22940                     this.minViewMode = 1;
22941                     break;
22942                 case 'years':
22943                     this.minViewMode = 2;
22944                     break;
22945                 default:
22946                     this.minViewMode = 0;
22947                     break;
22948             }
22949         }
22950         
22951         if (typeof(this.viewMode === 'string')) {
22952             switch (this.viewMode) {
22953                 case 'months':
22954                     this.viewMode = 1;
22955                     break;
22956                 case 'years':
22957                     this.viewMode = 2;
22958                     break;
22959                 default:
22960                     this.viewMode = 0;
22961                     break;
22962             }
22963         }
22964                 
22965         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22966         
22967 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22968         
22969         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22970         
22971         this.picker().on('mousedown', this.onMousedown, this);
22972         this.picker().on('click', this.onClick, this);
22973         
22974         this.picker().addClass('datepicker-dropdown');
22975         
22976         this.startViewMode = this.viewMode;
22977         
22978         if(this.singleMode){
22979             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22980                 v.setVisibilityMode(Roo.Element.DISPLAY);
22981                 v.hide();
22982             });
22983             
22984             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22985                 v.setStyle('width', '189px');
22986             });
22987         }
22988         
22989         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22990             if(!this.calendarWeeks){
22991                 v.remove();
22992                 return;
22993             }
22994             
22995             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22996             v.attr('colspan', function(i, val){
22997                 return parseInt(val) + 1;
22998             });
22999         });
23000                         
23001         
23002         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23003         
23004         this.setStartDate(this.startDate);
23005         this.setEndDate(this.endDate);
23006         
23007         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23008         
23009         this.fillDow();
23010         this.fillMonths();
23011         this.update();
23012         this.showMode();
23013         
23014         if(this.isInline) {
23015             this.showPopup();
23016         }
23017     },
23018     
23019     picker : function()
23020     {
23021         return this.pickerEl;
23022 //        return this.el.select('.datepicker', true).first();
23023     },
23024     
23025     fillDow: function()
23026     {
23027         var dowCnt = this.weekStart;
23028         
23029         var dow = {
23030             tag: 'tr',
23031             cn: [
23032                 
23033             ]
23034         };
23035         
23036         if(this.calendarWeeks){
23037             dow.cn.push({
23038                 tag: 'th',
23039                 cls: 'cw',
23040                 html: '&nbsp;'
23041             })
23042         }
23043         
23044         while (dowCnt < this.weekStart + 7) {
23045             dow.cn.push({
23046                 tag: 'th',
23047                 cls: 'dow',
23048                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23049             });
23050         }
23051         
23052         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23053     },
23054     
23055     fillMonths: function()
23056     {    
23057         var i = 0;
23058         var months = this.picker().select('>.datepicker-months td', true).first();
23059         
23060         months.dom.innerHTML = '';
23061         
23062         while (i < 12) {
23063             var month = {
23064                 tag: 'span',
23065                 cls: 'month',
23066                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23067             };
23068             
23069             months.createChild(month);
23070         }
23071         
23072     },
23073     
23074     update: function()
23075     {
23076         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;
23077         
23078         if (this.date < this.startDate) {
23079             this.viewDate = new Date(this.startDate);
23080         } else if (this.date > this.endDate) {
23081             this.viewDate = new Date(this.endDate);
23082         } else {
23083             this.viewDate = new Date(this.date);
23084         }
23085         
23086         this.fill();
23087     },
23088     
23089     fill: function() 
23090     {
23091         var d = new Date(this.viewDate),
23092                 year = d.getUTCFullYear(),
23093                 month = d.getUTCMonth(),
23094                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23095                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23096                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23097                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23098                 currentDate = this.date && this.date.valueOf(),
23099                 today = this.UTCToday();
23100         
23101         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23102         
23103 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23104         
23105 //        this.picker.select('>tfoot th.today').
23106 //                                              .text(dates[this.language].today)
23107 //                                              .toggle(this.todayBtn !== false);
23108     
23109         this.updateNavArrows();
23110         this.fillMonths();
23111                                                 
23112         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23113         
23114         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23115          
23116         prevMonth.setUTCDate(day);
23117         
23118         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23119         
23120         var nextMonth = new Date(prevMonth);
23121         
23122         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23123         
23124         nextMonth = nextMonth.valueOf();
23125         
23126         var fillMonths = false;
23127         
23128         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23129         
23130         while(prevMonth.valueOf() <= nextMonth) {
23131             var clsName = '';
23132             
23133             if (prevMonth.getUTCDay() === this.weekStart) {
23134                 if(fillMonths){
23135                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23136                 }
23137                     
23138                 fillMonths = {
23139                     tag: 'tr',
23140                     cn: []
23141                 };
23142                 
23143                 if(this.calendarWeeks){
23144                     // ISO 8601: First week contains first thursday.
23145                     // ISO also states week starts on Monday, but we can be more abstract here.
23146                     var
23147                     // Start of current week: based on weekstart/current date
23148                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23149                     // Thursday of this week
23150                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23151                     // First Thursday of year, year from thursday
23152                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23153                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23154                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23155                     
23156                     fillMonths.cn.push({
23157                         tag: 'td',
23158                         cls: 'cw',
23159                         html: calWeek
23160                     });
23161                 }
23162             }
23163             
23164             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23165                 clsName += ' old';
23166             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23167                 clsName += ' new';
23168             }
23169             if (this.todayHighlight &&
23170                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23171                 prevMonth.getUTCMonth() == today.getMonth() &&
23172                 prevMonth.getUTCDate() == today.getDate()) {
23173                 clsName += ' today';
23174             }
23175             
23176             if (currentDate && prevMonth.valueOf() === currentDate) {
23177                 clsName += ' active';
23178             }
23179             
23180             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23181                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23182                     clsName += ' disabled';
23183             }
23184             
23185             fillMonths.cn.push({
23186                 tag: 'td',
23187                 cls: 'day ' + clsName,
23188                 html: prevMonth.getDate()
23189             });
23190             
23191             prevMonth.setDate(prevMonth.getDate()+1);
23192         }
23193           
23194         var currentYear = this.date && this.date.getUTCFullYear();
23195         var currentMonth = this.date && this.date.getUTCMonth();
23196         
23197         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23198         
23199         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23200             v.removeClass('active');
23201             
23202             if(currentYear === year && k === currentMonth){
23203                 v.addClass('active');
23204             }
23205             
23206             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23207                 v.addClass('disabled');
23208             }
23209             
23210         });
23211         
23212         
23213         year = parseInt(year/10, 10) * 10;
23214         
23215         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23216         
23217         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23218         
23219         year -= 1;
23220         for (var i = -1; i < 11; i++) {
23221             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23222                 tag: 'span',
23223                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23224                 html: year
23225             });
23226             
23227             year += 1;
23228         }
23229     },
23230     
23231     showMode: function(dir) 
23232     {
23233         if (dir) {
23234             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23235         }
23236         
23237         Roo.each(this.picker().select('>div',true).elements, function(v){
23238             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23239             v.hide();
23240         });
23241         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23242     },
23243     
23244     place: function()
23245     {
23246         if(this.isInline) {
23247             return;
23248         }
23249         
23250         this.picker().removeClass(['bottom', 'top']);
23251         
23252         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23253             /*
23254              * place to the top of element!
23255              *
23256              */
23257             
23258             this.picker().addClass('top');
23259             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23260             
23261             return;
23262         }
23263         
23264         this.picker().addClass('bottom');
23265         
23266         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23267     },
23268     
23269     parseDate : function(value)
23270     {
23271         if(!value || value instanceof Date){
23272             return value;
23273         }
23274         var v = Date.parseDate(value, this.format);
23275         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23276             v = Date.parseDate(value, 'Y-m-d');
23277         }
23278         if(!v && this.altFormats){
23279             if(!this.altFormatsArray){
23280                 this.altFormatsArray = this.altFormats.split("|");
23281             }
23282             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23283                 v = Date.parseDate(value, this.altFormatsArray[i]);
23284             }
23285         }
23286         return v;
23287     },
23288     
23289     formatDate : function(date, fmt)
23290     {   
23291         return (!date || !(date instanceof Date)) ?
23292         date : date.dateFormat(fmt || this.format);
23293     },
23294     
23295     onFocus : function()
23296     {
23297         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23298         this.showPopup();
23299     },
23300     
23301     onBlur : function()
23302     {
23303         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23304         
23305         var d = this.inputEl().getValue();
23306         
23307         this.setValue(d);
23308                 
23309         this.hidePopup();
23310     },
23311     
23312     showPopup : function()
23313     {
23314         this.picker().show();
23315         this.update();
23316         this.place();
23317         
23318         this.fireEvent('showpopup', this, this.date);
23319     },
23320     
23321     hidePopup : function()
23322     {
23323         if(this.isInline) {
23324             return;
23325         }
23326         this.picker().hide();
23327         this.viewMode = this.startViewMode;
23328         this.showMode();
23329         
23330         this.fireEvent('hidepopup', this, this.date);
23331         
23332     },
23333     
23334     onMousedown: function(e)
23335     {
23336         e.stopPropagation();
23337         e.preventDefault();
23338     },
23339     
23340     keyup: function(e)
23341     {
23342         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23343         this.update();
23344     },
23345
23346     setValue: function(v)
23347     {
23348         if(this.fireEvent('beforeselect', this, v) !== false){
23349             var d = new Date(this.parseDate(v) ).clearTime();
23350         
23351             if(isNaN(d.getTime())){
23352                 this.date = this.viewDate = '';
23353                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23354                 return;
23355             }
23356
23357             v = this.formatDate(d);
23358
23359             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23360
23361             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23362
23363             this.update();
23364
23365             this.fireEvent('select', this, this.date);
23366         }
23367     },
23368     
23369     getValue: function()
23370     {
23371         return this.formatDate(this.date);
23372     },
23373     
23374     fireKey: function(e)
23375     {
23376         if (!this.picker().isVisible()){
23377             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23378                 this.showPopup();
23379             }
23380             return;
23381         }
23382         
23383         var dateChanged = false,
23384         dir, day, month,
23385         newDate, newViewDate;
23386         
23387         switch(e.keyCode){
23388             case 27: // escape
23389                 this.hidePopup();
23390                 e.preventDefault();
23391                 break;
23392             case 37: // left
23393             case 39: // right
23394                 if (!this.keyboardNavigation) {
23395                     break;
23396                 }
23397                 dir = e.keyCode == 37 ? -1 : 1;
23398                 
23399                 if (e.ctrlKey){
23400                     newDate = this.moveYear(this.date, dir);
23401                     newViewDate = this.moveYear(this.viewDate, dir);
23402                 } else if (e.shiftKey){
23403                     newDate = this.moveMonth(this.date, dir);
23404                     newViewDate = this.moveMonth(this.viewDate, dir);
23405                 } else {
23406                     newDate = new Date(this.date);
23407                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23408                     newViewDate = new Date(this.viewDate);
23409                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23410                 }
23411                 if (this.dateWithinRange(newDate)){
23412                     this.date = newDate;
23413                     this.viewDate = newViewDate;
23414                     this.setValue(this.formatDate(this.date));
23415 //                    this.update();
23416                     e.preventDefault();
23417                     dateChanged = true;
23418                 }
23419                 break;
23420             case 38: // up
23421             case 40: // down
23422                 if (!this.keyboardNavigation) {
23423                     break;
23424                 }
23425                 dir = e.keyCode == 38 ? -1 : 1;
23426                 if (e.ctrlKey){
23427                     newDate = this.moveYear(this.date, dir);
23428                     newViewDate = this.moveYear(this.viewDate, dir);
23429                 } else if (e.shiftKey){
23430                     newDate = this.moveMonth(this.date, dir);
23431                     newViewDate = this.moveMonth(this.viewDate, dir);
23432                 } else {
23433                     newDate = new Date(this.date);
23434                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23435                     newViewDate = new Date(this.viewDate);
23436                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23437                 }
23438                 if (this.dateWithinRange(newDate)){
23439                     this.date = newDate;
23440                     this.viewDate = newViewDate;
23441                     this.setValue(this.formatDate(this.date));
23442 //                    this.update();
23443                     e.preventDefault();
23444                     dateChanged = true;
23445                 }
23446                 break;
23447             case 13: // enter
23448                 this.setValue(this.formatDate(this.date));
23449                 this.hidePopup();
23450                 e.preventDefault();
23451                 break;
23452             case 9: // tab
23453                 this.setValue(this.formatDate(this.date));
23454                 this.hidePopup();
23455                 break;
23456             case 16: // shift
23457             case 17: // ctrl
23458             case 18: // alt
23459                 break;
23460             default :
23461                 this.hidePopup();
23462                 
23463         }
23464     },
23465     
23466     
23467     onClick: function(e) 
23468     {
23469         e.stopPropagation();
23470         e.preventDefault();
23471         
23472         var target = e.getTarget();
23473         
23474         if(target.nodeName.toLowerCase() === 'i'){
23475             target = Roo.get(target).dom.parentNode;
23476         }
23477         
23478         var nodeName = target.nodeName;
23479         var className = target.className;
23480         var html = target.innerHTML;
23481         //Roo.log(nodeName);
23482         
23483         switch(nodeName.toLowerCase()) {
23484             case 'th':
23485                 switch(className) {
23486                     case 'switch':
23487                         this.showMode(1);
23488                         break;
23489                     case 'prev':
23490                     case 'next':
23491                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23492                         switch(this.viewMode){
23493                                 case 0:
23494                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23495                                         break;
23496                                 case 1:
23497                                 case 2:
23498                                         this.viewDate = this.moveYear(this.viewDate, dir);
23499                                         break;
23500                         }
23501                         this.fill();
23502                         break;
23503                     case 'today':
23504                         var date = new Date();
23505                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23506 //                        this.fill()
23507                         this.setValue(this.formatDate(this.date));
23508                         
23509                         this.hidePopup();
23510                         break;
23511                 }
23512                 break;
23513             case 'span':
23514                 if (className.indexOf('disabled') < 0) {
23515                 if (!this.viewDate) {
23516                     this.viewDate = new Date();
23517                 }
23518                 this.viewDate.setUTCDate(1);
23519                     if (className.indexOf('month') > -1) {
23520                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23521                     } else {
23522                         var year = parseInt(html, 10) || 0;
23523                         this.viewDate.setUTCFullYear(year);
23524                         
23525                     }
23526                     
23527                     if(this.singleMode){
23528                         this.setValue(this.formatDate(this.viewDate));
23529                         this.hidePopup();
23530                         return;
23531                     }
23532                     
23533                     this.showMode(-1);
23534                     this.fill();
23535                 }
23536                 break;
23537                 
23538             case 'td':
23539                 //Roo.log(className);
23540                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23541                     var day = parseInt(html, 10) || 1;
23542                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23543                         month = (this.viewDate || new Date()).getUTCMonth();
23544
23545                     if (className.indexOf('old') > -1) {
23546                         if(month === 0 ){
23547                             month = 11;
23548                             year -= 1;
23549                         }else{
23550                             month -= 1;
23551                         }
23552                     } else if (className.indexOf('new') > -1) {
23553                         if (month == 11) {
23554                             month = 0;
23555                             year += 1;
23556                         } else {
23557                             month += 1;
23558                         }
23559                     }
23560                     //Roo.log([year,month,day]);
23561                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23562                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23563 //                    this.fill();
23564                     //Roo.log(this.formatDate(this.date));
23565                     this.setValue(this.formatDate(this.date));
23566                     this.hidePopup();
23567                 }
23568                 break;
23569         }
23570     },
23571     
23572     setStartDate: function(startDate)
23573     {
23574         this.startDate = startDate || -Infinity;
23575         if (this.startDate !== -Infinity) {
23576             this.startDate = this.parseDate(this.startDate);
23577         }
23578         this.update();
23579         this.updateNavArrows();
23580     },
23581
23582     setEndDate: function(endDate)
23583     {
23584         this.endDate = endDate || Infinity;
23585         if (this.endDate !== Infinity) {
23586             this.endDate = this.parseDate(this.endDate);
23587         }
23588         this.update();
23589         this.updateNavArrows();
23590     },
23591     
23592     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23593     {
23594         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23595         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23596             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23597         }
23598         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23599             return parseInt(d, 10);
23600         });
23601         this.update();
23602         this.updateNavArrows();
23603     },
23604     
23605     updateNavArrows: function() 
23606     {
23607         if(this.singleMode){
23608             return;
23609         }
23610         
23611         var d = new Date(this.viewDate),
23612         year = d.getUTCFullYear(),
23613         month = d.getUTCMonth();
23614         
23615         Roo.each(this.picker().select('.prev', true).elements, function(v){
23616             v.show();
23617             switch (this.viewMode) {
23618                 case 0:
23619
23620                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23621                         v.hide();
23622                     }
23623                     break;
23624                 case 1:
23625                 case 2:
23626                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23627                         v.hide();
23628                     }
23629                     break;
23630             }
23631         });
23632         
23633         Roo.each(this.picker().select('.next', true).elements, function(v){
23634             v.show();
23635             switch (this.viewMode) {
23636                 case 0:
23637
23638                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23639                         v.hide();
23640                     }
23641                     break;
23642                 case 1:
23643                 case 2:
23644                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23645                         v.hide();
23646                     }
23647                     break;
23648             }
23649         })
23650     },
23651     
23652     moveMonth: function(date, dir)
23653     {
23654         if (!dir) {
23655             return date;
23656         }
23657         var new_date = new Date(date.valueOf()),
23658         day = new_date.getUTCDate(),
23659         month = new_date.getUTCMonth(),
23660         mag = Math.abs(dir),
23661         new_month, test;
23662         dir = dir > 0 ? 1 : -1;
23663         if (mag == 1){
23664             test = dir == -1
23665             // If going back one month, make sure month is not current month
23666             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23667             ? function(){
23668                 return new_date.getUTCMonth() == month;
23669             }
23670             // If going forward one month, make sure month is as expected
23671             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23672             : function(){
23673                 return new_date.getUTCMonth() != new_month;
23674             };
23675             new_month = month + dir;
23676             new_date.setUTCMonth(new_month);
23677             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23678             if (new_month < 0 || new_month > 11) {
23679                 new_month = (new_month + 12) % 12;
23680             }
23681         } else {
23682             // For magnitudes >1, move one month at a time...
23683             for (var i=0; i<mag; i++) {
23684                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23685                 new_date = this.moveMonth(new_date, dir);
23686             }
23687             // ...then reset the day, keeping it in the new month
23688             new_month = new_date.getUTCMonth();
23689             new_date.setUTCDate(day);
23690             test = function(){
23691                 return new_month != new_date.getUTCMonth();
23692             };
23693         }
23694         // Common date-resetting loop -- if date is beyond end of month, make it
23695         // end of month
23696         while (test()){
23697             new_date.setUTCDate(--day);
23698             new_date.setUTCMonth(new_month);
23699         }
23700         return new_date;
23701     },
23702
23703     moveYear: function(date, dir)
23704     {
23705         return this.moveMonth(date, dir*12);
23706     },
23707
23708     dateWithinRange: function(date)
23709     {
23710         return date >= this.startDate && date <= this.endDate;
23711     },
23712
23713     
23714     remove: function() 
23715     {
23716         this.picker().remove();
23717     },
23718     
23719     validateValue : function(value)
23720     {
23721         if(this.getVisibilityEl().hasClass('hidden')){
23722             return true;
23723         }
23724         
23725         if(value.length < 1)  {
23726             if(this.allowBlank){
23727                 return true;
23728             }
23729             return false;
23730         }
23731         
23732         if(value.length < this.minLength){
23733             return false;
23734         }
23735         if(value.length > this.maxLength){
23736             return false;
23737         }
23738         if(this.vtype){
23739             var vt = Roo.form.VTypes;
23740             if(!vt[this.vtype](value, this)){
23741                 return false;
23742             }
23743         }
23744         if(typeof this.validator == "function"){
23745             var msg = this.validator(value);
23746             if(msg !== true){
23747                 return false;
23748             }
23749         }
23750         
23751         if(this.regex && !this.regex.test(value)){
23752             return false;
23753         }
23754         
23755         if(typeof(this.parseDate(value)) == 'undefined'){
23756             return false;
23757         }
23758         
23759         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23760             return false;
23761         }      
23762         
23763         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23764             return false;
23765         } 
23766         
23767         
23768         return true;
23769     },
23770     
23771     reset : function()
23772     {
23773         this.date = this.viewDate = '';
23774         
23775         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23776     }
23777    
23778 });
23779
23780 Roo.apply(Roo.bootstrap.form.DateField,  {
23781     
23782     head : {
23783         tag: 'thead',
23784         cn: [
23785         {
23786             tag: 'tr',
23787             cn: [
23788             {
23789                 tag: 'th',
23790                 cls: 'prev',
23791                 html: '<i class="fa fa-arrow-left"/>'
23792             },
23793             {
23794                 tag: 'th',
23795                 cls: 'switch',
23796                 colspan: '5'
23797             },
23798             {
23799                 tag: 'th',
23800                 cls: 'next',
23801                 html: '<i class="fa fa-arrow-right"/>'
23802             }
23803
23804             ]
23805         }
23806         ]
23807     },
23808     
23809     content : {
23810         tag: 'tbody',
23811         cn: [
23812         {
23813             tag: 'tr',
23814             cn: [
23815             {
23816                 tag: 'td',
23817                 colspan: '7'
23818             }
23819             ]
23820         }
23821         ]
23822     },
23823     
23824     footer : {
23825         tag: 'tfoot',
23826         cn: [
23827         {
23828             tag: 'tr',
23829             cn: [
23830             {
23831                 tag: 'th',
23832                 colspan: '7',
23833                 cls: 'today'
23834             }
23835                     
23836             ]
23837         }
23838         ]
23839     },
23840     
23841     dates:{
23842         en: {
23843             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23844             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23845             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23846             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23847             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23848             today: "Today"
23849         }
23850     },
23851     
23852     modes: [
23853     {
23854         clsName: 'days',
23855         navFnc: 'Month',
23856         navStep: 1
23857     },
23858     {
23859         clsName: 'months',
23860         navFnc: 'FullYear',
23861         navStep: 1
23862     },
23863     {
23864         clsName: 'years',
23865         navFnc: 'FullYear',
23866         navStep: 10
23867     }]
23868 });
23869
23870 Roo.apply(Roo.bootstrap.form.DateField,  {
23871   
23872     template : {
23873         tag: 'div',
23874         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23875         cn: [
23876         {
23877             tag: 'div',
23878             cls: 'datepicker-days',
23879             cn: [
23880             {
23881                 tag: 'table',
23882                 cls: 'table-condensed',
23883                 cn:[
23884                 Roo.bootstrap.form.DateField.head,
23885                 {
23886                     tag: 'tbody'
23887                 },
23888                 Roo.bootstrap.form.DateField.footer
23889                 ]
23890             }
23891             ]
23892         },
23893         {
23894             tag: 'div',
23895             cls: 'datepicker-months',
23896             cn: [
23897             {
23898                 tag: 'table',
23899                 cls: 'table-condensed',
23900                 cn:[
23901                 Roo.bootstrap.form.DateField.head,
23902                 Roo.bootstrap.form.DateField.content,
23903                 Roo.bootstrap.form.DateField.footer
23904                 ]
23905             }
23906             ]
23907         },
23908         {
23909             tag: 'div',
23910             cls: 'datepicker-years',
23911             cn: [
23912             {
23913                 tag: 'table',
23914                 cls: 'table-condensed',
23915                 cn:[
23916                 Roo.bootstrap.form.DateField.head,
23917                 Roo.bootstrap.form.DateField.content,
23918                 Roo.bootstrap.form.DateField.footer
23919                 ]
23920             }
23921             ]
23922         }
23923         ]
23924     }
23925 });
23926
23927  
23928
23929  /*
23930  * - LGPL
23931  *
23932  * TimeField
23933  * 
23934  */
23935
23936 /**
23937  * @class Roo.bootstrap.form.TimeField
23938  * @extends Roo.bootstrap.form.Input
23939  * Bootstrap DateField class
23940  * 
23941  * 
23942  * @constructor
23943  * Create a new TimeField
23944  * @param {Object} config The config object
23945  */
23946
23947 Roo.bootstrap.form.TimeField = function(config){
23948     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23949     this.addEvents({
23950             /**
23951              * @event show
23952              * Fires when this field show.
23953              * @param {Roo.bootstrap.form.DateField} thisthis
23954              * @param {Mixed} date The date value
23955              */
23956             show : true,
23957             /**
23958              * @event show
23959              * Fires when this field hide.
23960              * @param {Roo.bootstrap.form.DateField} this
23961              * @param {Mixed} date The date value
23962              */
23963             hide : true,
23964             /**
23965              * @event select
23966              * Fires when select a date.
23967              * @param {Roo.bootstrap.form.DateField} this
23968              * @param {Mixed} date The date value
23969              */
23970             select : true
23971         });
23972 };
23973
23974 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23975     
23976     /**
23977      * @cfg {String} format
23978      * The default time format string which can be overriden for localization support.  The format must be
23979      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23980      */
23981     format : "H:i",
23982
23983     getAutoCreate : function()
23984     {
23985         this.after = '<i class="fa far fa-clock"></i>';
23986         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23987         
23988          
23989     },
23990     onRender: function(ct, position)
23991     {
23992         
23993         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23994                 
23995         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23996         
23997         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23998         
23999         this.pop = this.picker().select('>.datepicker-time',true).first();
24000         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24001         
24002         this.picker().on('mousedown', this.onMousedown, this);
24003         this.picker().on('click', this.onClick, this);
24004         
24005         this.picker().addClass('datepicker-dropdown');
24006     
24007         this.fillTime();
24008         this.update();
24009             
24010         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24011         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24012         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24013         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24014         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24015         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24016
24017     },
24018     
24019     fireKey: function(e){
24020         if (!this.picker().isVisible()){
24021             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24022                 this.show();
24023             }
24024             return;
24025         }
24026
24027         e.preventDefault();
24028         
24029         switch(e.keyCode){
24030             case 27: // escape
24031                 this.hide();
24032                 break;
24033             case 37: // left
24034             case 39: // right
24035                 this.onTogglePeriod();
24036                 break;
24037             case 38: // up
24038                 this.onIncrementMinutes();
24039                 break;
24040             case 40: // down
24041                 this.onDecrementMinutes();
24042                 break;
24043             case 13: // enter
24044             case 9: // tab
24045                 this.setTime();
24046                 break;
24047         }
24048     },
24049     
24050     onClick: function(e) {
24051         e.stopPropagation();
24052         e.preventDefault();
24053     },
24054     
24055     picker : function()
24056     {
24057         return this.pickerEl;
24058     },
24059     
24060     fillTime: function()
24061     {    
24062         var time = this.pop.select('tbody', true).first();
24063         
24064         time.dom.innerHTML = '';
24065         
24066         time.createChild({
24067             tag: 'tr',
24068             cn: [
24069                 {
24070                     tag: 'td',
24071                     cn: [
24072                         {
24073                             tag: 'a',
24074                             href: '#',
24075                             cls: 'btn',
24076                             cn: [
24077                                 {
24078                                     tag: 'i',
24079                                     cls: 'hours-up fa fas fa-chevron-up'
24080                                 }
24081                             ]
24082                         } 
24083                     ]
24084                 },
24085                 {
24086                     tag: 'td',
24087                     cls: 'separator'
24088                 },
24089                 {
24090                     tag: 'td',
24091                     cn: [
24092                         {
24093                             tag: 'a',
24094                             href: '#',
24095                             cls: 'btn',
24096                             cn: [
24097                                 {
24098                                     tag: 'i',
24099                                     cls: 'minutes-up fa fas fa-chevron-up'
24100                                 }
24101                             ]
24102                         }
24103                     ]
24104                 },
24105                 {
24106                     tag: 'td',
24107                     cls: 'separator'
24108                 }
24109             ]
24110         });
24111         
24112         time.createChild({
24113             tag: 'tr',
24114             cn: [
24115                 {
24116                     tag: 'td',
24117                     cn: [
24118                         {
24119                             tag: 'span',
24120                             cls: 'timepicker-hour',
24121                             html: '00'
24122                         }  
24123                     ]
24124                 },
24125                 {
24126                     tag: 'td',
24127                     cls: 'separator',
24128                     html: ':'
24129                 },
24130                 {
24131                     tag: 'td',
24132                     cn: [
24133                         {
24134                             tag: 'span',
24135                             cls: 'timepicker-minute',
24136                             html: '00'
24137                         }  
24138                     ]
24139                 },
24140                 {
24141                     tag: 'td',
24142                     cls: 'separator'
24143                 },
24144                 {
24145                     tag: 'td',
24146                     cn: [
24147                         {
24148                             tag: 'button',
24149                             type: 'button',
24150                             cls: 'btn btn-primary period',
24151                             html: 'AM'
24152                             
24153                         }
24154                     ]
24155                 }
24156             ]
24157         });
24158         
24159         time.createChild({
24160             tag: 'tr',
24161             cn: [
24162                 {
24163                     tag: 'td',
24164                     cn: [
24165                         {
24166                             tag: 'a',
24167                             href: '#',
24168                             cls: 'btn',
24169                             cn: [
24170                                 {
24171                                     tag: 'span',
24172                                     cls: 'hours-down fa fas fa-chevron-down'
24173                                 }
24174                             ]
24175                         }
24176                     ]
24177                 },
24178                 {
24179                     tag: 'td',
24180                     cls: 'separator'
24181                 },
24182                 {
24183                     tag: 'td',
24184                     cn: [
24185                         {
24186                             tag: 'a',
24187                             href: '#',
24188                             cls: 'btn',
24189                             cn: [
24190                                 {
24191                                     tag: 'span',
24192                                     cls: 'minutes-down fa fas fa-chevron-down'
24193                                 }
24194                             ]
24195                         }
24196                     ]
24197                 },
24198                 {
24199                     tag: 'td',
24200                     cls: 'separator'
24201                 }
24202             ]
24203         });
24204         
24205     },
24206     
24207     update: function()
24208     {
24209         
24210         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24211         
24212         this.fill();
24213     },
24214     
24215     fill: function() 
24216     {
24217         var hours = this.time.getHours();
24218         var minutes = this.time.getMinutes();
24219         var period = 'AM';
24220         
24221         if(hours > 11){
24222             period = 'PM';
24223         }
24224         
24225         if(hours == 0){
24226             hours = 12;
24227         }
24228         
24229         
24230         if(hours > 12){
24231             hours = hours - 12;
24232         }
24233         
24234         if(hours < 10){
24235             hours = '0' + hours;
24236         }
24237         
24238         if(minutes < 10){
24239             minutes = '0' + minutes;
24240         }
24241         
24242         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24243         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24244         this.pop.select('button', true).first().dom.innerHTML = period;
24245         
24246     },
24247     
24248     place: function()
24249     {   
24250         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24251         
24252         var cls = ['bottom'];
24253         
24254         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24255             cls.pop();
24256             cls.push('top');
24257         }
24258         
24259         cls.push('right');
24260         
24261         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24262             cls.pop();
24263             cls.push('left');
24264         }
24265         //this.picker().setXY(20000,20000);
24266         this.picker().addClass(cls.join('-'));
24267         
24268         var _this = this;
24269         
24270         Roo.each(cls, function(c){
24271             if(c == 'bottom'){
24272                 (function() {
24273                  //  
24274                 }).defer(200);
24275                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24276                 //_this.picker().setTop(_this.inputEl().getHeight());
24277                 return;
24278             }
24279             if(c == 'top'){
24280                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24281                 
24282                 //_this.picker().setTop(0 - _this.picker().getHeight());
24283                 return;
24284             }
24285             /*
24286             if(c == 'left'){
24287                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24288                 return;
24289             }
24290             if(c == 'right'){
24291                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24292                 return;
24293             }
24294             */
24295         });
24296         
24297     },
24298   
24299     onFocus : function()
24300     {
24301         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24302         this.show();
24303     },
24304     
24305     onBlur : function()
24306     {
24307         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24308         this.hide();
24309     },
24310     
24311     show : function()
24312     {
24313         this.picker().show();
24314         this.pop.show();
24315         this.update();
24316         this.place();
24317         
24318         this.fireEvent('show', this, this.date);
24319     },
24320     
24321     hide : function()
24322     {
24323         this.picker().hide();
24324         this.pop.hide();
24325         
24326         this.fireEvent('hide', this, this.date);
24327     },
24328     
24329     setTime : function()
24330     {
24331         this.hide();
24332         this.setValue(this.time.format(this.format));
24333         
24334         this.fireEvent('select', this, this.date);
24335         
24336         
24337     },
24338     
24339     onMousedown: function(e){
24340         e.stopPropagation();
24341         e.preventDefault();
24342     },
24343     
24344     onIncrementHours: function()
24345     {
24346         Roo.log('onIncrementHours');
24347         this.time = this.time.add(Date.HOUR, 1);
24348         this.update();
24349         
24350     },
24351     
24352     onDecrementHours: function()
24353     {
24354         Roo.log('onDecrementHours');
24355         this.time = this.time.add(Date.HOUR, -1);
24356         this.update();
24357     },
24358     
24359     onIncrementMinutes: function()
24360     {
24361         Roo.log('onIncrementMinutes');
24362         this.time = this.time.add(Date.MINUTE, 1);
24363         this.update();
24364     },
24365     
24366     onDecrementMinutes: function()
24367     {
24368         Roo.log('onDecrementMinutes');
24369         this.time = this.time.add(Date.MINUTE, -1);
24370         this.update();
24371     },
24372     
24373     onTogglePeriod: function()
24374     {
24375         Roo.log('onTogglePeriod');
24376         this.time = this.time.add(Date.HOUR, 12);
24377         this.update();
24378     }
24379     
24380    
24381 });
24382  
24383
24384 Roo.apply(Roo.bootstrap.form.TimeField,  {
24385   
24386     template : {
24387         tag: 'div',
24388         cls: 'datepicker dropdown-menu',
24389         cn: [
24390             {
24391                 tag: 'div',
24392                 cls: 'datepicker-time',
24393                 cn: [
24394                 {
24395                     tag: 'table',
24396                     cls: 'table-condensed',
24397                     cn:[
24398                         {
24399                             tag: 'tbody',
24400                             cn: [
24401                                 {
24402                                     tag: 'tr',
24403                                     cn: [
24404                                     {
24405                                         tag: 'td',
24406                                         colspan: '7'
24407                                     }
24408                                     ]
24409                                 }
24410                             ]
24411                         },
24412                         {
24413                             tag: 'tfoot',
24414                             cn: [
24415                                 {
24416                                     tag: 'tr',
24417                                     cn: [
24418                                     {
24419                                         tag: 'th',
24420                                         colspan: '7',
24421                                         cls: '',
24422                                         cn: [
24423                                             {
24424                                                 tag: 'button',
24425                                                 cls: 'btn btn-info ok',
24426                                                 html: 'OK'
24427                                             }
24428                                         ]
24429                                     }
24430                     
24431                                     ]
24432                                 }
24433                             ]
24434                         }
24435                     ]
24436                 }
24437                 ]
24438             }
24439         ]
24440     }
24441 });
24442
24443  
24444
24445  /*
24446  * - LGPL
24447  *
24448  * MonthField
24449  * 
24450  */
24451
24452 /**
24453  * @class Roo.bootstrap.form.MonthField
24454  * @extends Roo.bootstrap.form.Input
24455  * Bootstrap MonthField class
24456  * 
24457  * @cfg {String} language default en
24458  * 
24459  * @constructor
24460  * Create a new MonthField
24461  * @param {Object} config The config object
24462  */
24463
24464 Roo.bootstrap.form.MonthField = function(config){
24465     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24466     
24467     this.addEvents({
24468         /**
24469          * @event show
24470          * Fires when this field show.
24471          * @param {Roo.bootstrap.form.MonthField} this
24472          * @param {Mixed} date The date value
24473          */
24474         show : true,
24475         /**
24476          * @event show
24477          * Fires when this field hide.
24478          * @param {Roo.bootstrap.form.MonthField} this
24479          * @param {Mixed} date The date value
24480          */
24481         hide : true,
24482         /**
24483          * @event select
24484          * Fires when select a date.
24485          * @param {Roo.bootstrap.form.MonthField} this
24486          * @param {String} oldvalue The old value
24487          * @param {String} newvalue The new value
24488          */
24489         select : true
24490     });
24491 };
24492
24493 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24494     
24495     onRender: function(ct, position)
24496     {
24497         
24498         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24499         
24500         this.language = this.language || 'en';
24501         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24502         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24503         
24504         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24505         this.isInline = false;
24506         this.isInput = true;
24507         this.component = this.el.select('.add-on', true).first() || false;
24508         this.component = (this.component && this.component.length === 0) ? false : this.component;
24509         this.hasInput = this.component && this.inputEL().length;
24510         
24511         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24512         
24513         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24514         
24515         this.picker().on('mousedown', this.onMousedown, this);
24516         this.picker().on('click', this.onClick, this);
24517         
24518         this.picker().addClass('datepicker-dropdown');
24519         
24520         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24521             v.setStyle('width', '189px');
24522         });
24523         
24524         this.fillMonths();
24525         
24526         this.update();
24527         
24528         if(this.isInline) {
24529             this.show();
24530         }
24531         
24532     },
24533     
24534     setValue: function(v, suppressEvent)
24535     {   
24536         var o = this.getValue();
24537         
24538         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24539         
24540         this.update();
24541
24542         if(suppressEvent !== true){
24543             this.fireEvent('select', this, o, v);
24544         }
24545         
24546     },
24547     
24548     getValue: function()
24549     {
24550         return this.value;
24551     },
24552     
24553     onClick: function(e) 
24554     {
24555         e.stopPropagation();
24556         e.preventDefault();
24557         
24558         var target = e.getTarget();
24559         
24560         if(target.nodeName.toLowerCase() === 'i'){
24561             target = Roo.get(target).dom.parentNode;
24562         }
24563         
24564         var nodeName = target.nodeName;
24565         var className = target.className;
24566         var html = target.innerHTML;
24567         
24568         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24569             return;
24570         }
24571         
24572         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24573         
24574         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24575         
24576         this.hide();
24577                         
24578     },
24579     
24580     picker : function()
24581     {
24582         return this.pickerEl;
24583     },
24584     
24585     fillMonths: function()
24586     {    
24587         var i = 0;
24588         var months = this.picker().select('>.datepicker-months td', true).first();
24589         
24590         months.dom.innerHTML = '';
24591         
24592         while (i < 12) {
24593             var month = {
24594                 tag: 'span',
24595                 cls: 'month',
24596                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24597             };
24598             
24599             months.createChild(month);
24600         }
24601         
24602     },
24603     
24604     update: function()
24605     {
24606         var _this = this;
24607         
24608         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24609             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24610         }
24611         
24612         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24613             e.removeClass('active');
24614             
24615             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24616                 e.addClass('active');
24617             }
24618         })
24619     },
24620     
24621     place: function()
24622     {
24623         if(this.isInline) {
24624             return;
24625         }
24626         
24627         this.picker().removeClass(['bottom', 'top']);
24628         
24629         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24630             /*
24631              * place to the top of element!
24632              *
24633              */
24634             
24635             this.picker().addClass('top');
24636             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24637             
24638             return;
24639         }
24640         
24641         this.picker().addClass('bottom');
24642         
24643         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24644     },
24645     
24646     onFocus : function()
24647     {
24648         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24649         this.show();
24650     },
24651     
24652     onBlur : function()
24653     {
24654         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24655         
24656         var d = this.inputEl().getValue();
24657         
24658         this.setValue(d);
24659                 
24660         this.hide();
24661     },
24662     
24663     show : function()
24664     {
24665         this.picker().show();
24666         this.picker().select('>.datepicker-months', true).first().show();
24667         this.update();
24668         this.place();
24669         
24670         this.fireEvent('show', this, this.date);
24671     },
24672     
24673     hide : function()
24674     {
24675         if(this.isInline) {
24676             return;
24677         }
24678         this.picker().hide();
24679         this.fireEvent('hide', this, this.date);
24680         
24681     },
24682     
24683     onMousedown: function(e)
24684     {
24685         e.stopPropagation();
24686         e.preventDefault();
24687     },
24688     
24689     keyup: function(e)
24690     {
24691         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24692         this.update();
24693     },
24694
24695     fireKey: function(e)
24696     {
24697         if (!this.picker().isVisible()){
24698             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24699                 this.show();
24700             }
24701             return;
24702         }
24703         
24704         var dir;
24705         
24706         switch(e.keyCode){
24707             case 27: // escape
24708                 this.hide();
24709                 e.preventDefault();
24710                 break;
24711             case 37: // left
24712             case 39: // right
24713                 dir = e.keyCode == 37 ? -1 : 1;
24714                 
24715                 this.vIndex = this.vIndex + dir;
24716                 
24717                 if(this.vIndex < 0){
24718                     this.vIndex = 0;
24719                 }
24720                 
24721                 if(this.vIndex > 11){
24722                     this.vIndex = 11;
24723                 }
24724                 
24725                 if(isNaN(this.vIndex)){
24726                     this.vIndex = 0;
24727                 }
24728                 
24729                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24730                 
24731                 break;
24732             case 38: // up
24733             case 40: // down
24734                 
24735                 dir = e.keyCode == 38 ? -1 : 1;
24736                 
24737                 this.vIndex = this.vIndex + dir * 4;
24738                 
24739                 if(this.vIndex < 0){
24740                     this.vIndex = 0;
24741                 }
24742                 
24743                 if(this.vIndex > 11){
24744                     this.vIndex = 11;
24745                 }
24746                 
24747                 if(isNaN(this.vIndex)){
24748                     this.vIndex = 0;
24749                 }
24750                 
24751                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24752                 break;
24753                 
24754             case 13: // enter
24755                 
24756                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24757                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24758                 }
24759                 
24760                 this.hide();
24761                 e.preventDefault();
24762                 break;
24763             case 9: // tab
24764                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24765                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24766                 }
24767                 this.hide();
24768                 break;
24769             case 16: // shift
24770             case 17: // ctrl
24771             case 18: // alt
24772                 break;
24773             default :
24774                 this.hide();
24775                 
24776         }
24777     },
24778     
24779     remove: function() 
24780     {
24781         this.picker().remove();
24782     }
24783    
24784 });
24785
24786 Roo.apply(Roo.bootstrap.form.MonthField,  {
24787     
24788     content : {
24789         tag: 'tbody',
24790         cn: [
24791         {
24792             tag: 'tr',
24793             cn: [
24794             {
24795                 tag: 'td',
24796                 colspan: '7'
24797             }
24798             ]
24799         }
24800         ]
24801     },
24802     
24803     dates:{
24804         en: {
24805             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24806             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24807         }
24808     }
24809 });
24810
24811 Roo.apply(Roo.bootstrap.form.MonthField,  {
24812   
24813     template : {
24814         tag: 'div',
24815         cls: 'datepicker dropdown-menu roo-dynamic',
24816         cn: [
24817             {
24818                 tag: 'div',
24819                 cls: 'datepicker-months',
24820                 cn: [
24821                 {
24822                     tag: 'table',
24823                     cls: 'table-condensed',
24824                     cn:[
24825                         Roo.bootstrap.form.DateField.content
24826                     ]
24827                 }
24828                 ]
24829             }
24830         ]
24831     }
24832 });
24833
24834  
24835
24836  
24837  /*
24838  * - LGPL
24839  *
24840  * CheckBox
24841  * 
24842  */
24843
24844 /**
24845  * @class Roo.bootstrap.form.CheckBox
24846  * @extends Roo.bootstrap.form.Input
24847  * Bootstrap CheckBox class
24848  * 
24849  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24850  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24851  * @cfg {String} boxLabel The text that appears beside the checkbox
24852  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24853  * @cfg {Boolean} checked initnal the element
24854  * @cfg {Boolean} inline inline the element (default false)
24855  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24856  * @cfg {String} tooltip label tooltip
24857  * 
24858  * @constructor
24859  * Create a new CheckBox
24860  * @param {Object} config The config object
24861  */
24862
24863 Roo.bootstrap.form.CheckBox = function(config){
24864     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24865    
24866     this.addEvents({
24867         /**
24868         * @event check
24869         * Fires when the element is checked or unchecked.
24870         * @param {Roo.bootstrap.form.CheckBox} this This input
24871         * @param {Boolean} checked The new checked value
24872         */
24873        check : true,
24874        /**
24875         * @event click
24876         * Fires when the element is click.
24877         * @param {Roo.bootstrap.form.CheckBox} this This input
24878         */
24879        click : true
24880     });
24881     
24882 };
24883
24884 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24885   
24886     inputType: 'checkbox',
24887     inputValue: 1,
24888     valueOff: 0,
24889     boxLabel: false,
24890     checked: false,
24891     weight : false,
24892     inline: false,
24893     tooltip : '',
24894     
24895     // checkbox success does not make any sense really.. 
24896     invalidClass : "",
24897     validClass : "",
24898     
24899     
24900     getAutoCreate : function()
24901     {
24902         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24903         
24904         var id = Roo.id();
24905         
24906         var cfg = {};
24907         
24908         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24909         
24910         if(this.inline){
24911             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24912         }
24913         
24914         var input =  {
24915             tag: 'input',
24916             id : id,
24917             type : this.inputType,
24918             value : this.inputValue,
24919             cls : 'roo-' + this.inputType, //'form-box',
24920             placeholder : this.placeholder || ''
24921             
24922         };
24923         
24924         if(this.inputType != 'radio'){
24925             var hidden =  {
24926                 tag: 'input',
24927                 type : 'hidden',
24928                 cls : 'roo-hidden-value',
24929                 value : this.checked ? this.inputValue : this.valueOff
24930             };
24931         }
24932         
24933             
24934         if (this.weight) { // Validity check?
24935             cfg.cls += " " + this.inputType + "-" + this.weight;
24936         }
24937         
24938         if (this.disabled) {
24939             input.disabled=true;
24940         }
24941         
24942         if(this.checked){
24943             input.checked = this.checked;
24944         }
24945         
24946         if (this.name) {
24947             
24948             input.name = this.name;
24949             
24950             if(this.inputType != 'radio'){
24951                 hidden.name = this.name;
24952                 input.name = '_hidden_' + this.name;
24953             }
24954         }
24955         
24956         if (this.size) {
24957             input.cls += ' input-' + this.size;
24958         }
24959         
24960         var settings=this;
24961         
24962         ['xs','sm','md','lg'].map(function(size){
24963             if (settings[size]) {
24964                 cfg.cls += ' col-' + size + '-' + settings[size];
24965             }
24966         });
24967         
24968         var inputblock = input;
24969          
24970         if (this.before || this.after) {
24971             
24972             inputblock = {
24973                 cls : 'input-group',
24974                 cn :  [] 
24975             };
24976             
24977             if (this.before) {
24978                 inputblock.cn.push({
24979                     tag :'span',
24980                     cls : 'input-group-addon',
24981                     html : this.before
24982                 });
24983             }
24984             
24985             inputblock.cn.push(input);
24986             
24987             if(this.inputType != 'radio'){
24988                 inputblock.cn.push(hidden);
24989             }
24990             
24991             if (this.after) {
24992                 inputblock.cn.push({
24993                     tag :'span',
24994                     cls : 'input-group-addon',
24995                     html : this.after
24996                 });
24997             }
24998             
24999         }
25000         var boxLabelCfg = false;
25001         
25002         if(this.boxLabel){
25003            
25004             boxLabelCfg = {
25005                 tag: 'label',
25006                 //'for': id, // box label is handled by onclick - so no for...
25007                 cls: 'box-label',
25008                 html: this.boxLabel
25009             };
25010             if(this.tooltip){
25011                 boxLabelCfg.tooltip = this.tooltip;
25012             }
25013              
25014         }
25015         
25016         
25017         if (align ==='left' && this.fieldLabel.length) {
25018 //                Roo.log("left and has label");
25019             cfg.cn = [
25020                 {
25021                     tag: 'label',
25022                     'for' :  id,
25023                     cls : 'control-label',
25024                     html : this.fieldLabel
25025                 },
25026                 {
25027                     cls : "", 
25028                     cn: [
25029                         inputblock
25030                     ]
25031                 }
25032             ];
25033             
25034             if (boxLabelCfg) {
25035                 cfg.cn[1].cn.push(boxLabelCfg);
25036             }
25037             
25038             if(this.labelWidth > 12){
25039                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25040             }
25041             
25042             if(this.labelWidth < 13 && this.labelmd == 0){
25043                 this.labelmd = this.labelWidth;
25044             }
25045             
25046             if(this.labellg > 0){
25047                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25048                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25049             }
25050             
25051             if(this.labelmd > 0){
25052                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25053                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25054             }
25055             
25056             if(this.labelsm > 0){
25057                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25058                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25059             }
25060             
25061             if(this.labelxs > 0){
25062                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25063                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25064             }
25065             
25066         } else if ( this.fieldLabel.length) {
25067 //                Roo.log(" label");
25068                 cfg.cn = [
25069                    
25070                     {
25071                         tag: this.boxLabel ? 'span' : 'label',
25072                         'for': id,
25073                         cls: 'control-label box-input-label',
25074                         //cls : 'input-group-addon',
25075                         html : this.fieldLabel
25076                     },
25077                     
25078                     inputblock
25079                     
25080                 ];
25081                 if (boxLabelCfg) {
25082                     cfg.cn.push(boxLabelCfg);
25083                 }
25084
25085         } else {
25086             
25087 //                Roo.log(" no label && no align");
25088                 cfg.cn = [  inputblock ] ;
25089                 if (boxLabelCfg) {
25090                     cfg.cn.push(boxLabelCfg);
25091                 }
25092
25093                 
25094         }
25095         
25096        
25097         
25098         if(this.inputType != 'radio'){
25099             cfg.cn.push(hidden);
25100         }
25101         
25102         return cfg;
25103         
25104     },
25105     
25106     /**
25107      * return the real input element.
25108      */
25109     inputEl: function ()
25110     {
25111         return this.el.select('input.roo-' + this.inputType,true).first();
25112     },
25113     hiddenEl: function ()
25114     {
25115         return this.el.select('input.roo-hidden-value',true).first();
25116     },
25117     
25118     labelEl: function()
25119     {
25120         return this.el.select('label.control-label',true).first();
25121     },
25122     /* depricated... */
25123     
25124     label: function()
25125     {
25126         return this.labelEl();
25127     },
25128     
25129     boxLabelEl: function()
25130     {
25131         return this.el.select('label.box-label',true).first();
25132     },
25133     
25134     initEvents : function()
25135     {
25136 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25137         
25138         this.inputEl().on('click', this.onClick,  this);
25139         
25140         if (this.boxLabel) { 
25141             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25142         }
25143         
25144         this.startValue = this.getValue();
25145         
25146         if(this.groupId){
25147             Roo.bootstrap.form.CheckBox.register(this);
25148         }
25149     },
25150     
25151     onClick : function(e)
25152     {   
25153         if(this.fireEvent('click', this, e) !== false){
25154             this.setChecked(!this.checked);
25155         }
25156         
25157     },
25158     
25159     setChecked : function(state,suppressEvent)
25160     {
25161         this.startValue = this.getValue();
25162
25163         if(this.inputType == 'radio'){
25164             
25165             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25166                 e.dom.checked = false;
25167             });
25168             
25169             this.inputEl().dom.checked = true;
25170             
25171             this.inputEl().dom.value = this.inputValue;
25172             
25173             if(suppressEvent !== true){
25174                 this.fireEvent('check', this, true);
25175             }
25176             
25177             this.validate();
25178             
25179             return;
25180         }
25181         
25182         this.checked = state;
25183         
25184         this.inputEl().dom.checked = state;
25185         
25186         
25187         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25188         
25189         if(suppressEvent !== true){
25190             this.fireEvent('check', this, state);
25191         }
25192         
25193         this.validate();
25194     },
25195     
25196     getValue : function()
25197     {
25198         if(this.inputType == 'radio'){
25199             return this.getGroupValue();
25200         }
25201         
25202         return this.hiddenEl().dom.value;
25203         
25204     },
25205     
25206     getGroupValue : function()
25207     {
25208         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25209             return '';
25210         }
25211         
25212         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25213     },
25214     
25215     setValue : function(v,suppressEvent)
25216     {
25217         if(this.inputType == 'radio'){
25218             this.setGroupValue(v, suppressEvent);
25219             return;
25220         }
25221         
25222         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25223         
25224         this.validate();
25225     },
25226     
25227     setGroupValue : function(v, suppressEvent)
25228     {
25229         this.startValue = this.getValue();
25230         
25231         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25232             e.dom.checked = false;
25233             
25234             if(e.dom.value == v){
25235                 e.dom.checked = true;
25236             }
25237         });
25238         
25239         if(suppressEvent !== true){
25240             this.fireEvent('check', this, true);
25241         }
25242
25243         this.validate();
25244         
25245         return;
25246     },
25247     
25248     validate : function()
25249     {
25250         if(this.getVisibilityEl().hasClass('hidden')){
25251             return true;
25252         }
25253         
25254         if(
25255                 this.disabled || 
25256                 (this.inputType == 'radio' && this.validateRadio()) ||
25257                 (this.inputType == 'checkbox' && this.validateCheckbox())
25258         ){
25259             this.markValid();
25260             return true;
25261         }
25262         
25263         this.markInvalid();
25264         return false;
25265     },
25266     
25267     validateRadio : function()
25268     {
25269         if(this.getVisibilityEl().hasClass('hidden')){
25270             return true;
25271         }
25272         
25273         if(this.allowBlank){
25274             return true;
25275         }
25276         
25277         var valid = false;
25278         
25279         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25280             if(!e.dom.checked){
25281                 return;
25282             }
25283             
25284             valid = true;
25285             
25286             return false;
25287         });
25288         
25289         return valid;
25290     },
25291     
25292     validateCheckbox : function()
25293     {
25294         if(!this.groupId){
25295             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25296             //return (this.getValue() == this.inputValue) ? true : false;
25297         }
25298         
25299         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25300         
25301         if(!group){
25302             return false;
25303         }
25304         
25305         var r = false;
25306         
25307         for(var i in group){
25308             if(group[i].el.isVisible(true)){
25309                 r = false;
25310                 break;
25311             }
25312             
25313             r = true;
25314         }
25315         
25316         for(var i in group){
25317             if(r){
25318                 break;
25319             }
25320             
25321             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25322         }
25323         
25324         return r;
25325     },
25326     
25327     /**
25328      * Mark this field as valid
25329      */
25330     markValid : function()
25331     {
25332         var _this = this;
25333         
25334         this.fireEvent('valid', this);
25335         
25336         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25337         
25338         if(this.groupId){
25339             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25340         }
25341         
25342         if(label){
25343             label.markValid();
25344         }
25345
25346         if(this.inputType == 'radio'){
25347             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25348                 var fg = e.findParent('.form-group', false, true);
25349                 if (Roo.bootstrap.version == 3) {
25350                     fg.removeClass([_this.invalidClass, _this.validClass]);
25351                     fg.addClass(_this.validClass);
25352                 } else {
25353                     fg.removeClass(['is-valid', 'is-invalid']);
25354                     fg.addClass('is-valid');
25355                 }
25356             });
25357             
25358             return;
25359         }
25360
25361         if(!this.groupId){
25362             var fg = this.el.findParent('.form-group', false, true);
25363             if (Roo.bootstrap.version == 3) {
25364                 fg.removeClass([this.invalidClass, this.validClass]);
25365                 fg.addClass(this.validClass);
25366             } else {
25367                 fg.removeClass(['is-valid', 'is-invalid']);
25368                 fg.addClass('is-valid');
25369             }
25370             return;
25371         }
25372         
25373         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25374         
25375         if(!group){
25376             return;
25377         }
25378         
25379         for(var i in group){
25380             var fg = group[i].el.findParent('.form-group', false, true);
25381             if (Roo.bootstrap.version == 3) {
25382                 fg.removeClass([this.invalidClass, this.validClass]);
25383                 fg.addClass(this.validClass);
25384             } else {
25385                 fg.removeClass(['is-valid', 'is-invalid']);
25386                 fg.addClass('is-valid');
25387             }
25388         }
25389     },
25390     
25391      /**
25392      * Mark this field as invalid
25393      * @param {String} msg The validation message
25394      */
25395     markInvalid : function(msg)
25396     {
25397         if(this.allowBlank){
25398             return;
25399         }
25400         
25401         var _this = this;
25402         
25403         this.fireEvent('invalid', this, msg);
25404         
25405         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25406         
25407         if(this.groupId){
25408             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25409         }
25410         
25411         if(label){
25412             label.markInvalid();
25413         }
25414             
25415         if(this.inputType == 'radio'){
25416             
25417             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25418                 var fg = e.findParent('.form-group', false, true);
25419                 if (Roo.bootstrap.version == 3) {
25420                     fg.removeClass([_this.invalidClass, _this.validClass]);
25421                     fg.addClass(_this.invalidClass);
25422                 } else {
25423                     fg.removeClass(['is-invalid', 'is-valid']);
25424                     fg.addClass('is-invalid');
25425                 }
25426             });
25427             
25428             return;
25429         }
25430         
25431         if(!this.groupId){
25432             var fg = this.el.findParent('.form-group', false, true);
25433             if (Roo.bootstrap.version == 3) {
25434                 fg.removeClass([_this.invalidClass, _this.validClass]);
25435                 fg.addClass(_this.invalidClass);
25436             } else {
25437                 fg.removeClass(['is-invalid', 'is-valid']);
25438                 fg.addClass('is-invalid');
25439             }
25440             return;
25441         }
25442         
25443         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25444         
25445         if(!group){
25446             return;
25447         }
25448         
25449         for(var i in group){
25450             var fg = group[i].el.findParent('.form-group', false, true);
25451             if (Roo.bootstrap.version == 3) {
25452                 fg.removeClass([_this.invalidClass, _this.validClass]);
25453                 fg.addClass(_this.invalidClass);
25454             } else {
25455                 fg.removeClass(['is-invalid', 'is-valid']);
25456                 fg.addClass('is-invalid');
25457             }
25458         }
25459         
25460     },
25461     
25462     clearInvalid : function()
25463     {
25464         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25465         
25466         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25467         
25468         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25469         
25470         if (label && label.iconEl) {
25471             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25472             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25473         }
25474     },
25475     
25476     disable : function()
25477     {
25478         if(this.inputType != 'radio'){
25479             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25480             return;
25481         }
25482         
25483         var _this = this;
25484         
25485         if(this.rendered){
25486             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25487                 _this.getActionEl().addClass(this.disabledClass);
25488                 e.dom.disabled = true;
25489             });
25490         }
25491         
25492         this.disabled = true;
25493         this.fireEvent("disable", this);
25494         return this;
25495     },
25496
25497     enable : function()
25498     {
25499         if(this.inputType != 'radio'){
25500             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25501             return;
25502         }
25503         
25504         var _this = this;
25505         
25506         if(this.rendered){
25507             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25508                 _this.getActionEl().removeClass(this.disabledClass);
25509                 e.dom.disabled = false;
25510             });
25511         }
25512         
25513         this.disabled = false;
25514         this.fireEvent("enable", this);
25515         return this;
25516     },
25517     
25518     setBoxLabel : function(v)
25519     {
25520         this.boxLabel = v;
25521         
25522         if(this.rendered){
25523             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25524         }
25525     }
25526
25527 });
25528
25529 Roo.apply(Roo.bootstrap.form.CheckBox, {
25530     
25531     groups: {},
25532     
25533      /**
25534     * register a CheckBox Group
25535     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25536     */
25537     register : function(checkbox)
25538     {
25539         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25540             this.groups[checkbox.groupId] = {};
25541         }
25542         
25543         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25544             return;
25545         }
25546         
25547         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25548         
25549     },
25550     /**
25551     * fetch a CheckBox Group based on the group ID
25552     * @param {string} the group ID
25553     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25554     */
25555     get: function(groupId) {
25556         if (typeof(this.groups[groupId]) == 'undefined') {
25557             return false;
25558         }
25559         
25560         return this.groups[groupId] ;
25561     }
25562     
25563     
25564 });
25565 /*
25566  * - LGPL
25567  *
25568  * RadioItem
25569  * 
25570  */
25571
25572 /**
25573  * @class Roo.bootstrap.form.Radio
25574  * @extends Roo.bootstrap.Component
25575  * Bootstrap Radio class
25576  * @cfg {String} boxLabel - the label associated
25577  * @cfg {String} value - the value of radio
25578  * 
25579  * @constructor
25580  * Create a new Radio
25581  * @param {Object} config The config object
25582  */
25583 Roo.bootstrap.form.Radio = function(config){
25584     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25585     
25586 };
25587
25588 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25589     
25590     boxLabel : '',
25591     
25592     value : '',
25593     
25594     getAutoCreate : function()
25595     {
25596         var cfg = {
25597             tag : 'div',
25598             cls : 'form-group radio',
25599             cn : [
25600                 {
25601                     tag : 'label',
25602                     cls : 'box-label',
25603                     html : this.boxLabel
25604                 }
25605             ]
25606         };
25607         
25608         return cfg;
25609     },
25610     
25611     initEvents : function() 
25612     {
25613         this.parent().register(this);
25614         
25615         this.el.on('click', this.onClick, this);
25616         
25617     },
25618     
25619     onClick : function(e)
25620     {
25621         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25622             this.setChecked(true);
25623         }
25624     },
25625     
25626     setChecked : function(state, suppressEvent)
25627     {
25628         this.parent().setValue(this.value, suppressEvent);
25629         
25630     },
25631     
25632     setBoxLabel : function(v)
25633     {
25634         this.boxLabel = v;
25635         
25636         if(this.rendered){
25637             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25638         }
25639     }
25640     
25641 });
25642  
25643
25644  /*
25645  * - LGPL
25646  *
25647  * Input
25648  * 
25649  */
25650
25651 /**
25652  * @class Roo.bootstrap.form.SecurePass
25653  * @extends Roo.bootstrap.form.Input
25654  * Bootstrap SecurePass class
25655  *
25656  * 
25657  * @constructor
25658  * Create a new SecurePass
25659  * @param {Object} config The config object
25660  */
25661  
25662 Roo.bootstrap.form.SecurePass = function (config) {
25663     // these go here, so the translation tool can replace them..
25664     this.errors = {
25665         PwdEmpty: "Please type a password, and then retype it to confirm.",
25666         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25667         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25668         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25669         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25670         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25671         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25672         TooWeak: "Your password is Too Weak."
25673     },
25674     this.meterLabel = "Password strength:";
25675     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25676     this.meterClass = [
25677         "roo-password-meter-tooweak", 
25678         "roo-password-meter-weak", 
25679         "roo-password-meter-medium", 
25680         "roo-password-meter-strong", 
25681         "roo-password-meter-grey"
25682     ];
25683     
25684     this.errors = {};
25685     
25686     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25687 }
25688
25689 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25690     /**
25691      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25692      * {
25693      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25694      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25695      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25696      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25697      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25698      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25699      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25700      * })
25701      */
25702     // private
25703     
25704     meterWidth: 300,
25705     errorMsg :'',    
25706     errors: false,
25707     imageRoot: '/',
25708     /**
25709      * @cfg {String/Object} Label for the strength meter (defaults to
25710      * 'Password strength:')
25711      */
25712     // private
25713     meterLabel: '',
25714     /**
25715      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25716      * ['Weak', 'Medium', 'Strong'])
25717      */
25718     // private    
25719     pwdStrengths: false,    
25720     // private
25721     strength: 0,
25722     // private
25723     _lastPwd: null,
25724     // private
25725     kCapitalLetter: 0,
25726     kSmallLetter: 1,
25727     kDigit: 2,
25728     kPunctuation: 3,
25729     
25730     insecure: false,
25731     // private
25732     initEvents: function ()
25733     {
25734         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25735
25736         if (this.el.is('input[type=password]') && Roo.isSafari) {
25737             this.el.on('keydown', this.SafariOnKeyDown, this);
25738         }
25739
25740         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25741     },
25742     // private
25743     onRender: function (ct, position)
25744     {
25745         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25746         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25747         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25748
25749         this.trigger.createChild({
25750                    cn: [
25751                     {
25752                     //id: 'PwdMeter',
25753                     tag: 'div',
25754                     cls: 'roo-password-meter-grey col-xs-12',
25755                     style: {
25756                         //width: 0,
25757                         //width: this.meterWidth + 'px'                                                
25758                         }
25759                     },
25760                     {                            
25761                          cls: 'roo-password-meter-text'                          
25762                     }
25763                 ]            
25764         });
25765
25766          
25767         if (this.hideTrigger) {
25768             this.trigger.setDisplayed(false);
25769         }
25770         this.setSize(this.width || '', this.height || '');
25771     },
25772     // private
25773     onDestroy: function ()
25774     {
25775         if (this.trigger) {
25776             this.trigger.removeAllListeners();
25777             this.trigger.remove();
25778         }
25779         if (this.wrap) {
25780             this.wrap.remove();
25781         }
25782         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25783     },
25784     // private
25785     checkStrength: function ()
25786     {
25787         var pwd = this.inputEl().getValue();
25788         if (pwd == this._lastPwd) {
25789             return;
25790         }
25791
25792         var strength;
25793         if (this.ClientSideStrongPassword(pwd)) {
25794             strength = 3;
25795         } else if (this.ClientSideMediumPassword(pwd)) {
25796             strength = 2;
25797         } else if (this.ClientSideWeakPassword(pwd)) {
25798             strength = 1;
25799         } else {
25800             strength = 0;
25801         }
25802         
25803         Roo.log('strength1: ' + strength);
25804         
25805         //var pm = this.trigger.child('div/div/div').dom;
25806         var pm = this.trigger.child('div/div');
25807         pm.removeClass(this.meterClass);
25808         pm.addClass(this.meterClass[strength]);
25809                 
25810         
25811         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25812                 
25813         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25814         
25815         this._lastPwd = pwd;
25816     },
25817     reset: function ()
25818     {
25819         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25820         
25821         this._lastPwd = '';
25822         
25823         var pm = this.trigger.child('div/div');
25824         pm.removeClass(this.meterClass);
25825         pm.addClass('roo-password-meter-grey');        
25826         
25827         
25828         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25829         
25830         pt.innerHTML = '';
25831         this.inputEl().dom.type='password';
25832     },
25833     // private
25834     validateValue: function (value)
25835     {
25836         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25837             return false;
25838         }
25839         if (value.length == 0) {
25840             if (this.allowBlank) {
25841                 this.clearInvalid();
25842                 return true;
25843             }
25844
25845             this.markInvalid(this.errors.PwdEmpty);
25846             this.errorMsg = this.errors.PwdEmpty;
25847             return false;
25848         }
25849         
25850         if(this.insecure){
25851             return true;
25852         }
25853         
25854         if (!value.match(/[\x21-\x7e]+/)) {
25855             this.markInvalid(this.errors.PwdBadChar);
25856             this.errorMsg = this.errors.PwdBadChar;
25857             return false;
25858         }
25859         if (value.length < 6) {
25860             this.markInvalid(this.errors.PwdShort);
25861             this.errorMsg = this.errors.PwdShort;
25862             return false;
25863         }
25864         if (value.length > 16) {
25865             this.markInvalid(this.errors.PwdLong);
25866             this.errorMsg = this.errors.PwdLong;
25867             return false;
25868         }
25869         var strength;
25870         if (this.ClientSideStrongPassword(value)) {
25871             strength = 3;
25872         } else if (this.ClientSideMediumPassword(value)) {
25873             strength = 2;
25874         } else if (this.ClientSideWeakPassword(value)) {
25875             strength = 1;
25876         } else {
25877             strength = 0;
25878         }
25879
25880         
25881         if (strength < 2) {
25882             //this.markInvalid(this.errors.TooWeak);
25883             this.errorMsg = this.errors.TooWeak;
25884             //return false;
25885         }
25886         
25887         
25888         console.log('strength2: ' + strength);
25889         
25890         //var pm = this.trigger.child('div/div/div').dom;
25891         
25892         var pm = this.trigger.child('div/div');
25893         pm.removeClass(this.meterClass);
25894         pm.addClass(this.meterClass[strength]);
25895                 
25896         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25897                 
25898         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25899         
25900         this.errorMsg = ''; 
25901         return true;
25902     },
25903     // private
25904     CharacterSetChecks: function (type)
25905     {
25906         this.type = type;
25907         this.fResult = false;
25908     },
25909     // private
25910     isctype: function (character, type)
25911     {
25912         switch (type) {  
25913             case this.kCapitalLetter:
25914                 if (character >= 'A' && character <= 'Z') {
25915                     return true;
25916                 }
25917                 break;
25918             
25919             case this.kSmallLetter:
25920                 if (character >= 'a' && character <= 'z') {
25921                     return true;
25922                 }
25923                 break;
25924             
25925             case this.kDigit:
25926                 if (character >= '0' && character <= '9') {
25927                     return true;
25928                 }
25929                 break;
25930             
25931             case this.kPunctuation:
25932                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25933                     return true;
25934                 }
25935                 break;
25936             
25937             default:
25938                 return false;
25939         }
25940
25941     },
25942     // private
25943     IsLongEnough: function (pwd, size)
25944     {
25945         return !(pwd == null || isNaN(size) || pwd.length < size);
25946     },
25947     // private
25948     SpansEnoughCharacterSets: function (word, nb)
25949     {
25950         if (!this.IsLongEnough(word, nb))
25951         {
25952             return false;
25953         }
25954
25955         var characterSetChecks = new Array(
25956             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25957             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25958         );
25959         
25960         for (var index = 0; index < word.length; ++index) {
25961             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25962                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25963                     characterSetChecks[nCharSet].fResult = true;
25964                     break;
25965                 }
25966             }
25967         }
25968
25969         var nCharSets = 0;
25970         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25971             if (characterSetChecks[nCharSet].fResult) {
25972                 ++nCharSets;
25973             }
25974         }
25975
25976         if (nCharSets < nb) {
25977             return false;
25978         }
25979         return true;
25980     },
25981     // private
25982     ClientSideStrongPassword: function (pwd)
25983     {
25984         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25985     },
25986     // private
25987     ClientSideMediumPassword: function (pwd)
25988     {
25989         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25990     },
25991     // private
25992     ClientSideWeakPassword: function (pwd)
25993     {
25994         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25995     }
25996           
25997 });
25998 Roo.htmleditor = {};
25999  
26000 /**
26001  * @class Roo.htmleditor.Filter
26002  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26003  * @cfg {DomElement} node The node to iterate and filter
26004  * @cfg {boolean|String|Array} tag Tags to replace 
26005  * @constructor
26006  * Create a new Filter.
26007  * @param {Object} config Configuration options
26008  */
26009
26010
26011
26012 Roo.htmleditor.Filter = function(cfg) {
26013     Roo.apply(this.cfg);
26014     // this does not actually call walk as it's really just a abstract class
26015 }
26016
26017
26018 Roo.htmleditor.Filter.prototype = {
26019     
26020     node: false,
26021     
26022     tag: false,
26023
26024     // overrride to do replace comments.
26025     replaceComment : false,
26026     
26027     // overrride to do replace or do stuff with tags..
26028     replaceTag : false,
26029     
26030     walk : function(dom)
26031     {
26032         Roo.each( Array.from(dom.childNodes), function( e ) {
26033             switch(true) {
26034                 
26035                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
26036                     this.replaceComment(e);
26037                     return;
26038                 
26039                 case e.nodeType != 1: //not a node.
26040                     return;
26041                 
26042                 case this.tag === true: // everything
26043                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26044                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26045                     if (this.replaceTag && false === this.replaceTag(e)) {
26046                         return;
26047                     }
26048                     if (e.hasChildNodes()) {
26049                         this.walk(e);
26050                     }
26051                     return;
26052                 
26053                 default:    // tags .. that do not match.
26054                     if (e.hasChildNodes()) {
26055                         this.walk(e);
26056                     }
26057             }
26058             
26059         }, this);
26060         
26061     }
26062 }; 
26063
26064 /**
26065  * @class Roo.htmleditor.FilterAttributes
26066  * clean attributes and  styles including http:// etc.. in attribute
26067  * @constructor
26068 * Run a new Attribute Filter
26069 * @param {Object} config Configuration options
26070  */
26071 Roo.htmleditor.FilterAttributes = function(cfg)
26072 {
26073     Roo.apply(this, cfg);
26074     this.attrib_black = this.attrib_black || [];
26075     this.attrib_white = this.attrib_white || [];
26076
26077     this.attrib_clean = this.attrib_clean || [];
26078     this.style_white = this.style_white || [];
26079     this.style_black = this.style_black || [];
26080     this.walk(cfg.node);
26081 }
26082
26083 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26084 {
26085     tag: true, // all tags
26086     
26087     attrib_black : false, // array
26088     attrib_clean : false,
26089     attrib_white : false,
26090
26091     style_white : false,
26092     style_black : false,
26093      
26094      
26095     replaceTag : function(node)
26096     {
26097         if (!node.attributes || !node.attributes.length) {
26098             return true;
26099         }
26100         
26101         for (var i = node.attributes.length-1; i > -1 ; i--) {
26102             var a = node.attributes[i];
26103             //console.log(a);
26104             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26105                 node.removeAttribute(a.name);
26106                 continue;
26107             }
26108             
26109             
26110             
26111             if (a.name.toLowerCase().substr(0,2)=='on')  {
26112                 node.removeAttribute(a.name);
26113                 continue;
26114             }
26115             
26116             
26117             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26118                 node.removeAttribute(a.name);
26119                 continue;
26120             }
26121             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26122                 this.cleanAttr(node,a.name,a.value); // fixme..
26123                 continue;
26124             }
26125             if (a.name == 'style') {
26126                 this.cleanStyle(node,a.name,a.value);
26127                 continue;
26128             }
26129             /// clean up MS crap..
26130             // tecnically this should be a list of valid class'es..
26131             
26132             
26133             if (a.name == 'class') {
26134                 if (a.value.match(/^Mso/)) {
26135                     node.removeAttribute('class');
26136                 }
26137                 
26138                 if (a.value.match(/^body$/)) {
26139                     node.removeAttribute('class');
26140                 }
26141                 continue;
26142             }
26143             
26144             
26145             // style cleanup!?
26146             // class cleanup?
26147             
26148         }
26149         return true; // clean children
26150     },
26151         
26152     cleanAttr: function(node, n,v)
26153     {
26154         
26155         if (v.match(/^\./) || v.match(/^\//)) {
26156             return;
26157         }
26158         if (v.match(/^(http|https):\/\//)
26159             || v.match(/^mailto:/) 
26160             || v.match(/^ftp:/)
26161             || v.match(/^data:/)
26162             ) {
26163             return;
26164         }
26165         if (v.match(/^#/)) {
26166             return;
26167         }
26168         if (v.match(/^\{/)) { // allow template editing.
26169             return;
26170         }
26171 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26172         node.removeAttribute(n);
26173         
26174     },
26175     cleanStyle : function(node,  n,v)
26176     {
26177         if (v.match(/expression/)) { //XSS?? should we even bother..
26178             node.removeAttribute(n);
26179             return;
26180         }
26181         
26182         var parts = v.split(/;/);
26183         var clean = [];
26184         
26185         Roo.each(parts, function(p) {
26186             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26187             if (!p.length) {
26188                 return true;
26189             }
26190             var l = p.split(':').shift().replace(/\s+/g,'');
26191             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26192             
26193             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26194                 return true;
26195             }
26196             //Roo.log()
26197             // only allow 'c whitelisted system attributes'
26198             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26199                 return true;
26200             }
26201             
26202             
26203             clean.push(p);
26204             return true;
26205         },this);
26206         if (clean.length) { 
26207             node.setAttribute(n, clean.join(';'));
26208         } else {
26209             node.removeAttribute(n);
26210         }
26211         
26212     }
26213         
26214         
26215         
26216     
26217 });/**
26218  * @class Roo.htmleditor.FilterBlack
26219  * remove blacklisted elements.
26220  * @constructor
26221  * Run a new Blacklisted Filter
26222  * @param {Object} config Configuration options
26223  */
26224
26225 Roo.htmleditor.FilterBlack = function(cfg)
26226 {
26227     Roo.apply(this, cfg);
26228     this.walk(cfg.node);
26229 }
26230
26231 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26232 {
26233     tag : true, // all elements.
26234    
26235     replaceTag : function(n)
26236     {
26237         n.parentNode.removeChild(n);
26238     }
26239 });
26240 /**
26241  * @class Roo.htmleditor.FilterComment
26242  * remove comments.
26243  * @constructor
26244 * Run a new Comments Filter
26245 * @param {Object} config Configuration options
26246  */
26247 Roo.htmleditor.FilterComment = function(cfg)
26248 {
26249     this.walk(cfg.node);
26250 }
26251
26252 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26253 {
26254   
26255     replaceComment : function(n)
26256     {
26257         n.parentNode.removeChild(n);
26258     }
26259 });/**
26260  * @class Roo.htmleditor.FilterKeepChildren
26261  * remove tags but keep children
26262  * @constructor
26263  * Run a new Keep Children Filter
26264  * @param {Object} config Configuration options
26265  */
26266
26267 Roo.htmleditor.FilterKeepChildren = function(cfg)
26268 {
26269     Roo.apply(this, cfg);
26270     if (this.tag === false) {
26271         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26272     }
26273     this.walk(cfg.node);
26274 }
26275
26276 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26277 {
26278     
26279   
26280     replaceTag : function(node)
26281     {
26282         // walk children...
26283         //Roo.log(node);
26284         var ar = Array.from(node.childNodes);
26285         //remove first..
26286         for (var i = 0; i < ar.length; i++) {
26287             if (ar[i].nodeType == 1) {
26288                 if (
26289                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
26290                     || // array and it matches
26291                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
26292                 ) {
26293                     this.replaceTag(ar[i]); // child is blacklisted as well...
26294                     continue;
26295                 }
26296             }
26297         }  
26298         ar = Array.from(node.childNodes);
26299         for (var i = 0; i < ar.length; i++) {
26300          
26301             node.removeChild(ar[i]);
26302             // what if we need to walk these???
26303             node.parentNode.insertBefore(ar[i], node);
26304             if (this.tag !== false) {
26305                 this.walk(ar[i]);
26306                 
26307             }
26308         }
26309         node.parentNode.removeChild(node);
26310         return false; // don't walk children
26311         
26312         
26313     }
26314 });/**
26315  * @class Roo.htmleditor.FilterParagraph
26316  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26317  * like on 'push' to remove the <p> tags and replace them with line breaks.
26318  * @constructor
26319  * Run a new Paragraph Filter
26320  * @param {Object} config Configuration options
26321  */
26322
26323 Roo.htmleditor.FilterParagraph = function(cfg)
26324 {
26325     // no need to apply config.
26326     this.walk(cfg.node);
26327 }
26328
26329 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26330 {
26331     
26332      
26333     tag : 'P',
26334     
26335      
26336     replaceTag : function(node)
26337     {
26338         
26339         if (node.childNodes.length == 1 &&
26340             node.childNodes[0].nodeType == 3 &&
26341             node.childNodes[0].textContent.trim().length < 1
26342             ) {
26343             // remove and replace with '<BR>';
26344             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26345             return false; // no need to walk..
26346         }
26347         var ar = Array.from(node.childNodes);
26348         for (var i = 0; i < ar.length; i++) {
26349             node.removeChild(ar[i]);
26350             // what if we need to walk these???
26351             node.parentNode.insertBefore(ar[i], node);
26352         }
26353         // now what about this?
26354         // <p> &nbsp; </p>
26355         
26356         // double BR.
26357         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26358         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26359         node.parentNode.removeChild(node);
26360         
26361         return false;
26362
26363     }
26364     
26365 });/**
26366  * @class Roo.htmleditor.FilterSpan
26367  * filter span's with no attributes out..
26368  * @constructor
26369  * Run a new Span Filter
26370  * @param {Object} config Configuration options
26371  */
26372
26373 Roo.htmleditor.FilterSpan = function(cfg)
26374 {
26375     // no need to apply config.
26376     this.walk(cfg.node);
26377 }
26378
26379 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26380 {
26381      
26382     tag : 'SPAN',
26383      
26384  
26385     replaceTag : function(node)
26386     {
26387         if (node.attributes && node.attributes.length > 0) {
26388             return true; // walk if there are any.
26389         }
26390         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26391         return false;
26392      
26393     }
26394     
26395 });/**
26396  * @class Roo.htmleditor.FilterTableWidth
26397   try and remove table width data - as that frequently messes up other stuff.
26398  * 
26399  *      was cleanTableWidths.
26400  *
26401  * Quite often pasting from word etc.. results in tables with column and widths.
26402  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26403  *
26404  * @constructor
26405  * Run a new Table Filter
26406  * @param {Object} config Configuration options
26407  */
26408
26409 Roo.htmleditor.FilterTableWidth = function(cfg)
26410 {
26411     // no need to apply config.
26412     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26413     this.walk(cfg.node);
26414 }
26415
26416 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26417 {
26418      
26419      
26420     
26421     replaceTag: function(node) {
26422         
26423         
26424       
26425         if (node.hasAttribute('width')) {
26426             node.removeAttribute('width');
26427         }
26428         
26429          
26430         if (node.hasAttribute("style")) {
26431             // pretty basic...
26432             
26433             var styles = node.getAttribute("style").split(";");
26434             var nstyle = [];
26435             Roo.each(styles, function(s) {
26436                 if (!s.match(/:/)) {
26437                     return;
26438                 }
26439                 var kv = s.split(":");
26440                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26441                     return;
26442                 }
26443                 // what ever is left... we allow.
26444                 nstyle.push(s);
26445             });
26446             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26447             if (!nstyle.length) {
26448                 node.removeAttribute('style');
26449             }
26450         }
26451         
26452         return true; // continue doing children..
26453     }
26454 });/**
26455  * @class Roo.htmleditor.FilterWord
26456  * try and clean up all the mess that Word generates.
26457  * 
26458  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
26459  
26460  * @constructor
26461  * Run a new Span Filter
26462  * @param {Object} config Configuration options
26463  */
26464
26465 Roo.htmleditor.FilterWord = function(cfg)
26466 {
26467     // no need to apply config.
26468     this.replaceDocBullets(cfg.node);
26469     
26470    // this.walk(cfg.node);
26471     
26472     
26473 }
26474
26475 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
26476 {
26477     tag: true,
26478      
26479     
26480     /**
26481      * Clean up MS wordisms...
26482      */
26483     replaceTag : function(node)
26484     {
26485          
26486         // no idea what this does - span with text, replaceds with just text.
26487         if(
26488                 node.nodeName == 'SPAN' &&
26489                 !node.hasAttributes() &&
26490                 node.childNodes.length == 1 &&
26491                 node.firstChild.nodeName == "#text"  
26492         ) {
26493             var textNode = node.firstChild;
26494             node.removeChild(textNode);
26495             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26496                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26497             }
26498             node.parentNode.insertBefore(textNode, node);
26499             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26500                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26501             }
26502             
26503             node.parentNode.removeChild(node);
26504             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
26505         }
26506         
26507    
26508         
26509         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26510             node.parentNode.removeChild(node);
26511             return false; // dont do chidlren
26512         }
26513         //Roo.log(node.tagName);
26514         // remove - but keep children..
26515         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26516             //Roo.log('-- removed');
26517             while (node.childNodes.length) {
26518                 var cn = node.childNodes[0];
26519                 node.removeChild(cn);
26520                 node.parentNode.insertBefore(cn, node);
26521                 // move node to parent - and clean it..
26522                 if (cn.nodeType == 1) {
26523                     this.replaceTag(cn);
26524                 }
26525                 
26526             }
26527             node.parentNode.removeChild(node);
26528             /// no need to iterate chidlren = it's got none..
26529             //this.iterateChildren(node, this.cleanWord);
26530             return false; // no need to iterate children.
26531         }
26532         // clean styles
26533         if (node.className.length) {
26534             
26535             var cn = node.className.split(/\W+/);
26536             var cna = [];
26537             Roo.each(cn, function(cls) {
26538                 if (cls.match(/Mso[a-zA-Z]+/)) {
26539                     return;
26540                 }
26541                 cna.push(cls);
26542             });
26543             node.className = cna.length ? cna.join(' ') : '';
26544             if (!cna.length) {
26545                 node.removeAttribute("class");
26546             }
26547         }
26548         
26549         if (node.hasAttribute("lang")) {
26550             node.removeAttribute("lang");
26551         }
26552         
26553         if (node.hasAttribute("style")) {
26554             
26555             var styles = node.getAttribute("style").split(";");
26556             var nstyle = [];
26557             Roo.each(styles, function(s) {
26558                 if (!s.match(/:/)) {
26559                     return;
26560                 }
26561                 var kv = s.split(":");
26562                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26563                     return;
26564                 }
26565                 // what ever is left... we allow.
26566                 nstyle.push(s);
26567             });
26568             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26569             if (!nstyle.length) {
26570                 node.removeAttribute('style');
26571             }
26572         }
26573         return true; // do children
26574         
26575         
26576         
26577     },
26578     
26579     styleToObject: function(node)
26580     {
26581         var styles = (node.getAttribute("style") || '').split(";");
26582         var ret = {};
26583         Roo.each(styles, function(s) {
26584             if (!s.match(/:/)) {
26585                 return;
26586             }
26587             var kv = s.split(":");
26588              
26589             // what ever is left... we allow.
26590             ret[kv[0].trim()] = kv[1];
26591         });
26592         return ret;
26593     },
26594     
26595     
26596     replaceDocBullets : function(doc)
26597     {
26598         // this is a bit odd - but it appears some indents use ql-indent-1
26599         
26600         var listpara = doc.getElementsByClassName('ql-indent-1');
26601         while(listpara.length) {
26602             this.replaceDocBullet(listpara.item(0));
26603         }
26604         
26605         var listpara = doc.getElementsByClassName('MsoListParagraph');
26606         while(listpara.length) {
26607             this.replaceDocBullet(listpara.item(0));
26608         }
26609     },
26610     
26611     replaceDocBullet : function(p)
26612     {
26613         // gather all the siblings.
26614         var ns = p,
26615             parent = p.parentNode,
26616             doc = parent.ownerDocument,
26617             items = [];
26618             
26619             
26620         while (ns) {
26621             if (ns.nodeType != 1) {
26622                 ns = ns.nextSibling;
26623                 continue;
26624             }
26625             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
26626                 break;
26627             }
26628             items.push(ns);
26629             ns = ns.nextSibling;
26630         }
26631         
26632         
26633         var ul = parent.ownerDocument.createElement('ul'); // what about number lists...
26634         parent.insertBefore(ul, p);
26635         var lvl = 0;
26636         var stack = [ ul ];
26637         var last_li = false;
26638         
26639         items.forEach(function(n, ipos) {
26640             //Roo.log("got innertHMLT=" + n.innerHTML);
26641             
26642             var spans = n.getElementsByTagName('span');
26643             if (!spans.length) {
26644                 //Roo.log("No spans found");
26645
26646                 parent.removeChild(n);
26647                 return; // skip it...
26648             }
26649            
26650                 
26651             
26652             var style = {};
26653             for(var i = 0; i < spans.length; i++) {
26654             
26655                 style = this.styleToObject(spans[i]);
26656                 if (typeof(style['mso-list']) == 'undefined') {
26657                     continue;
26658                 }
26659                 
26660                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
26661                 break;
26662             }
26663             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
26664             style = this.styleToObject(n); // mo-list is from the parent node.
26665             if (typeof(style['mso-list']) == 'undefined') {
26666                 //Roo.log("parent is missing level");
26667                 parent.removeChild(n);
26668                 return;
26669             }
26670             
26671             var nlvl =   (style['mso-list'].split(' ')[1].replace(/level/,'') *1) - 1  ;
26672             
26673             
26674             
26675             if (nlvl > lvl) {
26676                 //new indent
26677                 var nul = doc.createElement('ul'); // what about number lists...
26678                 last_li.appendChild(nul);
26679                 stack[nlvl] = nul;
26680                 
26681             }
26682             lvl = nlvl;
26683             
26684             var nli = stack[nlvl].appendChild(doc.createElement('li'));
26685             last_li = nli;
26686             nli.innerHTML = n.innerHTML;
26687             //Roo.log("innerHTML = " + n.innerHTML);
26688             parent.removeChild(n);
26689             
26690             // copy children of p into nli
26691             /*while(n.firstChild) {
26692                 var fc = n.firstChild;
26693                 n.removeChild(fc);
26694                 nli.appendChild(fc);
26695             }*/
26696              
26697             
26698         },this);
26699         
26700         
26701         
26702         
26703     }
26704     
26705     
26706     
26707 });
26708 /**
26709  * @class Roo.htmleditor.FilterStyleToTag
26710  * part of the word stuff... - certain 'styles' should be converted to tags.
26711  * eg.
26712  *   font-weight: bold -> bold
26713  *   ?? super / subscrit etc..
26714  * 
26715  * @constructor
26716 * Run a new style to tag filter.
26717 * @param {Object} config Configuration options
26718  */
26719 Roo.htmleditor.FilterStyleToTag = function(cfg)
26720 {
26721     
26722     this.tags = {
26723         B  : [ 'fontWeight' , 'bold'],
26724         I :  [ 'fontStyle' , 'italic'],
26725         //pre :  [ 'font-style' , 'italic'],
26726         // h1.. h6 ?? font-size?
26727         SUP : [ 'verticalAlign' , 'super' ],
26728         SUB : [ 'verticalAlign' , 'sub' ]
26729         
26730         
26731     };
26732     
26733     Roo.apply(this, cfg);
26734      
26735     
26736     this.walk(cfg.node);
26737     
26738     
26739     
26740 }
26741
26742
26743 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
26744 {
26745     tag: true, // all tags
26746     
26747     tags : false,
26748     
26749     
26750     replaceTag : function(node)
26751     {
26752         
26753         
26754         if (node.getAttribute("style") === null) {
26755             return true;
26756         }
26757         var inject = [];
26758         for (var k in this.tags) {
26759             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
26760                 inject.push(k);
26761                 node.style.removeProperty(this.tags[k][0]);
26762             }
26763         }
26764         if (!inject.length) {
26765             return true; 
26766         }
26767         var cn = Array.from(node.childNodes);
26768         var nn = node;
26769         Roo.each(inject, function(t) {
26770             var nc = node.ownerDocument.createElement(t);
26771             nn.appendChild(nc);
26772             nn = nc;
26773         });
26774         for(var i = 0;i < cn.length;cn++) {
26775             node.removeChild(cn[i]);
26776             nn.appendChild(cn[i]);
26777         }
26778         return true /// iterate thru
26779     }
26780     
26781 })/**
26782  * @class Roo.htmleditor.FilterLongBr
26783  * BR/BR/BR - keep a maximum of 2...
26784  * @constructor
26785  * Run a new Long BR Filter
26786  * @param {Object} config Configuration options
26787  */
26788
26789 Roo.htmleditor.FilterLongBr = function(cfg)
26790 {
26791     // no need to apply config.
26792     this.walk(cfg.node);
26793 }
26794
26795 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
26796 {
26797     
26798      
26799     tag : 'BR',
26800     
26801      
26802     replaceTag : function(node)
26803     {
26804         
26805         var ps = node.nextSibling;
26806         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26807             ps = ps.nextSibling;
26808         }
26809         
26810         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
26811             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
26812             return false;
26813         }
26814         
26815         if (!ps || ps.nodeType != 1) {
26816             return false;
26817         }
26818         
26819         if (!ps || ps.tagName != 'BR') {
26820            
26821             return false;
26822         }
26823         
26824         
26825         
26826         
26827         
26828         if (!node.previousSibling) {
26829             return false;
26830         }
26831         var ps = node.previousSibling;
26832         
26833         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26834             ps = ps.previousSibling;
26835         }
26836         if (!ps || ps.nodeType != 1) {
26837             return false;
26838         }
26839         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
26840         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
26841             return false;
26842         }
26843         
26844         node.parentNode.removeChild(node); // remove me...
26845         
26846         return false; // no need to do children
26847
26848     }
26849     
26850 }); 
26851
26852 /**
26853  * @class Roo.htmleditor.FilterBlock
26854  * removes id / data-block and contenteditable that are associated with blocks
26855  * usage should be done on a cloned copy of the dom
26856  * @constructor
26857 * Run a new Attribute Filter { node : xxxx }}
26858 * @param {Object} config Configuration options
26859  */
26860 Roo.htmleditor.FilterBlock = function(cfg)
26861 {
26862     Roo.apply(this, cfg);
26863     var qa = cfg.node.querySelectorAll;
26864     this.removeAttributes('data-block');
26865     this.removeAttributes('contenteditable');
26866     this.removeAttributes('id');
26867     
26868 }
26869
26870 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
26871 {
26872     node: true, // all tags
26873      
26874      
26875     removeAttributes : function(attr)
26876     {
26877         var ar = this.node.querySelectorAll('*[' + attr + ']');
26878         for (var i =0;i<ar.length;i++) {
26879             ar[i].removeAttribute(attr);
26880         }
26881     }
26882         
26883         
26884         
26885     
26886 });
26887 /**
26888  * @class Roo.htmleditor.KeyEnter
26889  * Handle Enter press..
26890  * @cfg {Roo.HtmlEditorCore} core the editor.
26891  * @constructor
26892  * Create a new Filter.
26893  * @param {Object} config Configuration options
26894  */
26895
26896
26897
26898
26899
26900 Roo.htmleditor.KeyEnter = function(cfg) {
26901     Roo.apply(this, cfg);
26902     // this does not actually call walk as it's really just a abstract class
26903  
26904     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
26905 }
26906
26907 //Roo.htmleditor.KeyEnter.i = 0;
26908
26909
26910 Roo.htmleditor.KeyEnter.prototype = {
26911     
26912     core : false,
26913     
26914     keypress : function(e)
26915     {
26916         if (e.charCode != 13 && e.charCode != 10) {
26917             Roo.log([e.charCode,e]);
26918             return true;
26919         }
26920         e.preventDefault();
26921         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
26922         var doc = this.core.doc;
26923           //add a new line
26924        
26925     
26926         var sel = this.core.getSelection();
26927         var range = sel.getRangeAt(0);
26928         var n = range.commonAncestorContainer;
26929         var pc = range.closest([ 'ol', 'ul']);
26930         var pli = range.closest('li');
26931         if (!pc || e.ctrlKey) {
26932             sel.insertNode('br', 'after'); 
26933          
26934             this.core.undoManager.addEvent();
26935             this.core.fireEditorEvent(e);
26936             return false;
26937         }
26938         
26939         // deal with <li> insetion
26940         if (pli.innerText.trim() == '' &&
26941             pli.previousSibling &&
26942             pli.previousSibling.nodeName == 'LI' &&
26943             pli.previousSibling.innerText.trim() ==  '') {
26944             pli.parentNode.removeChild(pli.previousSibling);
26945             sel.cursorAfter(pc);
26946             this.core.undoManager.addEvent();
26947             this.core.fireEditorEvent(e);
26948             return false;
26949         }
26950     
26951         var li = doc.createElement('LI');
26952         li.innerHTML = '&nbsp;';
26953         if (!pli || !pli.firstSibling) {
26954             pc.appendChild(li);
26955         } else {
26956             pli.parentNode.insertBefore(li, pli.firstSibling);
26957         }
26958         sel.cursorText (li.firstChild);
26959       
26960         this.core.undoManager.addEvent();
26961         this.core.fireEditorEvent(e);
26962
26963         return false;
26964         
26965     
26966         
26967         
26968          
26969     }
26970 };
26971      
26972 /**
26973  * @class Roo.htmleditor.Block
26974  * Base class for html editor blocks - do not use it directly .. extend it..
26975  * @cfg {DomElement} node The node to apply stuff to.
26976  * @cfg {String} friendly_name the name that appears in the context bar about this block
26977  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
26978  
26979  * @constructor
26980  * Create a new Filter.
26981  * @param {Object} config Configuration options
26982  */
26983
26984 Roo.htmleditor.Block  = function(cfg)
26985 {
26986     // do nothing .. should not be called really.
26987 }
26988 /**
26989  * factory method to get the block from an element (using cache if necessary)
26990  * @static
26991  * @param {HtmlElement} the dom element
26992  */
26993 Roo.htmleditor.Block.factory = function(node)
26994 {
26995     var cc = Roo.htmleditor.Block.cache;
26996     var id = Roo.get(node).id;
26997     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
26998         Roo.htmleditor.Block.cache[id].readElement(node);
26999         return Roo.htmleditor.Block.cache[id];
27000     }
27001     var db  = node.getAttribute('data-block');
27002     if (!db) {
27003         db = node.nodeName.toLowerCase().toUpperCaseFirst();
27004     }
27005     var cls = Roo.htmleditor['Block' + db];
27006     if (typeof(cls) == 'undefined') {
27007         //Roo.log(node.getAttribute('data-block'));
27008         Roo.log("OOps missing block : " + 'Block' + db);
27009         return false;
27010     }
27011     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
27012     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
27013 };
27014
27015 /**
27016  * initalize all Elements from content that are 'blockable'
27017  * @static
27018  * @param the body element
27019  */
27020 Roo.htmleditor.Block.initAll = function(body, type)
27021 {
27022     if (typeof(type) == 'undefined') {
27023         var ia = Roo.htmleditor.Block.initAll;
27024         ia(body,'table');
27025         ia(body,'td');
27026         ia(body,'figure');
27027         return;
27028     }
27029     Roo.each(Roo.get(body).query(type), function(e) {
27030         Roo.htmleditor.Block.factory(e);    
27031     },this);
27032 };
27033 // question goes here... do we need to clear out this cache sometimes?
27034 // or show we make it relivant to the htmleditor.
27035 Roo.htmleditor.Block.cache = {};
27036
27037 Roo.htmleditor.Block.prototype = {
27038     
27039     node : false,
27040     
27041      // used by context menu
27042     friendly_name : 'Based Block',
27043     
27044     // text for button to delete this element
27045     deleteTitle : false,
27046     
27047     context : false,
27048     /**
27049      * Update a node with values from this object
27050      * @param {DomElement} node
27051      */
27052     updateElement : function(node)
27053     {
27054         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
27055     },
27056      /**
27057      * convert to plain HTML for calling insertAtCursor..
27058      */
27059     toHTML : function()
27060     {
27061         return Roo.DomHelper.markup(this.toObject());
27062     },
27063     /**
27064      * used by readEleemnt to extract data from a node
27065      * may need improving as it's pretty basic
27066      
27067      * @param {DomElement} node
27068      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
27069      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
27070      * @param {String} style the style property - eg. text-align
27071      */
27072     getVal : function(node, tag, attr, style)
27073     {
27074         var n = node;
27075         if (tag !== true && n.tagName != tag.toUpperCase()) {
27076             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
27077             // but kiss for now.
27078             n = node.getElementsByTagName(tag).item(0);
27079         }
27080         if (!n) {
27081             return '';
27082         }
27083         if (attr === false) {
27084             return n;
27085         }
27086         if (attr == 'html') {
27087             return n.innerHTML;
27088         }
27089         if (attr == 'style') {
27090             return n.style[style]; 
27091         }
27092         
27093         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
27094             
27095     },
27096     /**
27097      * create a DomHelper friendly object - for use with 
27098      * Roo.DomHelper.markup / overwrite / etc..
27099      * (override this)
27100      */
27101     toObject : function()
27102     {
27103         return {};
27104     },
27105       /**
27106      * Read a node that has a 'data-block' property - and extract the values from it.
27107      * @param {DomElement} node - the node
27108      */
27109     readElement : function(node)
27110     {
27111         
27112     } 
27113     
27114     
27115 };
27116
27117  
27118
27119 /**
27120  * @class Roo.htmleditor.BlockFigure
27121  * Block that has an image and a figcaption
27122  * @cfg {String} image_src the url for the image
27123  * @cfg {String} align (left|right) alignment for the block default left
27124  * @cfg {String} caption the text to appear below  (and in the alt tag)
27125  * @cfg {String} caption_display (block|none) display or not the caption
27126  * @cfg {String|number} image_width the width of the image number or %?
27127  * @cfg {String|number} image_height the height of the image number or %?
27128  * 
27129  * @constructor
27130  * Create a new Filter.
27131  * @param {Object} config Configuration options
27132  */
27133
27134 Roo.htmleditor.BlockFigure = function(cfg)
27135 {
27136     if (cfg.node) {
27137         this.readElement(cfg.node);
27138         this.updateElement(cfg.node);
27139     }
27140     Roo.apply(this, cfg);
27141 }
27142 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
27143  
27144     
27145     // setable values.
27146     image_src: '',
27147     align: 'center',
27148     caption : '',
27149     caption_display : 'block',
27150     width : '100%',
27151     cls : '',
27152     href: '',
27153     video_url : '',
27154     
27155     // margin: '2%', not used
27156     
27157     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
27158
27159     
27160     // used by context menu
27161     friendly_name : 'Image with caption',
27162     deleteTitle : "Delete Image and Caption",
27163     
27164     contextMenu : function(toolbar)
27165     {
27166         
27167         var block = function() {
27168             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
27169         };
27170         
27171         
27172         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
27173         
27174         var syncValue = toolbar.editorcore.syncValue;
27175         
27176         var fields = {};
27177         
27178         return [
27179              {
27180                 xtype : 'TextItem',
27181                 text : "Source: ",
27182                 xns : rooui.Toolbar  //Boostrap?
27183             },
27184             {
27185                 xtype : 'Button',
27186                 text: 'Change Image URL',
27187                  
27188                 listeners : {
27189                     click: function (btn, state)
27190                     {
27191                         var b = block();
27192                         
27193                         Roo.MessageBox.show({
27194                             title : "Image Source URL",
27195                             msg : "Enter the url for the image",
27196                             buttons: Roo.MessageBox.OKCANCEL,
27197                             fn: function(btn, val){
27198                                 if (btn != 'ok') {
27199                                     return;
27200                                 }
27201                                 b.image_src = val;
27202                                 b.updateElement();
27203                                 syncValue();
27204                                 toolbar.editorcore.onEditorEvent();
27205                             },
27206                             minWidth:250,
27207                             prompt:true,
27208                             //multiline: multiline,
27209                             modal : true,
27210                             value : b.image_src
27211                         });
27212                     }
27213                 },
27214                 xns : rooui.Toolbar
27215             },
27216          
27217             {
27218                 xtype : 'Button',
27219                 text: 'Change Link URL',
27220                  
27221                 listeners : {
27222                     click: function (btn, state)
27223                     {
27224                         var b = block();
27225                         
27226                         Roo.MessageBox.show({
27227                             title : "Link URL",
27228                             msg : "Enter the url for the link - leave blank to have no link",
27229                             buttons: Roo.MessageBox.OKCANCEL,
27230                             fn: function(btn, val){
27231                                 if (btn != 'ok') {
27232                                     return;
27233                                 }
27234                                 b.href = val;
27235                                 b.updateElement();
27236                                 syncValue();
27237                                 toolbar.editorcore.onEditorEvent();
27238                             },
27239                             minWidth:250,
27240                             prompt:true,
27241                             //multiline: multiline,
27242                             modal : true,
27243                             value : b.href
27244                         });
27245                     }
27246                 },
27247                 xns : rooui.Toolbar
27248             },
27249             {
27250                 xtype : 'Button',
27251                 text: 'Show Video URL',
27252                  
27253                 listeners : {
27254                     click: function (btn, state)
27255                     {
27256                         Roo.MessageBox.alert("Video URL",
27257                             block().video_url == '' ? 'This image is not linked ot a video' :
27258                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
27259                     }
27260                 },
27261                 xns : rooui.Toolbar
27262             },
27263             
27264             
27265             {
27266                 xtype : 'TextItem',
27267                 text : "Width: ",
27268                 xns : rooui.Toolbar  //Boostrap?
27269             },
27270             {
27271                 xtype : 'ComboBox',
27272                 allowBlank : false,
27273                 displayField : 'val',
27274                 editable : true,
27275                 listWidth : 100,
27276                 triggerAction : 'all',
27277                 typeAhead : true,
27278                 valueField : 'val',
27279                 width : 70,
27280                 name : 'width',
27281                 listeners : {
27282                     select : function (combo, r, index)
27283                     {
27284                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27285                         var b = block();
27286                         b.width = r.get('val');
27287                         b.updateElement();
27288                         syncValue();
27289                         toolbar.editorcore.onEditorEvent();
27290                     }
27291                 },
27292                 xns : rooui.form,
27293                 store : {
27294                     xtype : 'SimpleStore',
27295                     data : [
27296                         ['50%'],
27297                         ['80%'],
27298                         ['100%']
27299                     ],
27300                     fields : [ 'val'],
27301                     xns : Roo.data
27302                 }
27303             },
27304             {
27305                 xtype : 'TextItem',
27306                 text : "Align: ",
27307                 xns : rooui.Toolbar  //Boostrap?
27308             },
27309             {
27310                 xtype : 'ComboBox',
27311                 allowBlank : false,
27312                 displayField : 'val',
27313                 editable : true,
27314                 listWidth : 100,
27315                 triggerAction : 'all',
27316                 typeAhead : true,
27317                 valueField : 'val',
27318                 width : 70,
27319                 name : 'align',
27320                 listeners : {
27321                     select : function (combo, r, index)
27322                     {
27323                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27324                         var b = block();
27325                         b.align = r.get('val');
27326                         b.updateElement();
27327                         syncValue();
27328                         toolbar.editorcore.onEditorEvent();
27329                     }
27330                 },
27331                 xns : rooui.form,
27332                 store : {
27333                     xtype : 'SimpleStore',
27334                     data : [
27335                         ['left'],
27336                         ['right'],
27337                         ['center']
27338                     ],
27339                     fields : [ 'val'],
27340                     xns : Roo.data
27341                 }
27342             },
27343             
27344             
27345             {
27346                 xtype : 'Button',
27347                 text: 'Hide Caption',
27348                 name : 'caption_display',
27349                 pressed : false,
27350                 enableToggle : true,
27351                 setValue : function(v) {
27352                     // this trigger toggle.
27353                      
27354                     this.setText(v ? "Hide Caption" : "Show Caption");
27355                     this.setPressed(v != 'block');
27356                 },
27357                 listeners : {
27358                     toggle: function (btn, state)
27359                     {
27360                         var b  = block();
27361                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
27362                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
27363                         b.updateElement();
27364                         syncValue();
27365                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27366                         toolbar.editorcore.onEditorEvent();
27367                     }
27368                 },
27369                 xns : rooui.Toolbar
27370             }
27371         ];
27372         
27373     },
27374     /**
27375      * create a DomHelper friendly object - for use with
27376      * Roo.DomHelper.markup / overwrite / etc..
27377      */
27378     toObject : function()
27379     {
27380         var d = document.createElement('div');
27381         d.innerHTML = this.caption;
27382         
27383         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
27384         
27385         var iw = this.align == 'center' ? this.width : '100%';
27386         var img =   {
27387             tag : 'img',
27388             contenteditable : 'false',
27389             src : this.image_src,
27390             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
27391             style: {
27392                 width : iw,
27393                 maxWidth : iw + ' !important', // this is not getting rendered?
27394                 margin : m  
27395                 
27396             }
27397         };
27398         /*
27399         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
27400                     '<a href="{2}">' + 
27401                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
27402                     '</a>' + 
27403                 '</div>',
27404         */
27405                 
27406         if (this.href.length > 0) {
27407             img = {
27408                 tag : 'a',
27409                 href: this.href,
27410                 contenteditable : 'true',
27411                 cn : [
27412                     img
27413                 ]
27414             };
27415         }
27416         
27417         
27418         if (this.video_url.length > 0) {
27419             img = {
27420                 tag : 'div',
27421                 cls : this.cls,
27422                 frameborder : 0,
27423                 allowfullscreen : true,
27424                 width : 420,  // these are for video tricks - that we replace the outer
27425                 height : 315,
27426                 src : this.video_url,
27427                 cn : [
27428                     img
27429                 ]
27430             };
27431         }
27432         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
27433         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
27434         
27435   
27436         var ret =   {
27437             tag: 'figure',
27438             'data-block' : 'Figure',
27439             'data-width' : this.width, 
27440             contenteditable : 'false',
27441             
27442             style : {
27443                 display: 'block',
27444                 float :  this.align ,
27445                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
27446                 width : this.align == 'center' ? '100%' : this.width,
27447                 margin:  '0px',
27448                 padding: this.align == 'center' ? '0' : '0 10px' ,
27449                 textAlign : this.align   // seems to work for email..
27450                 
27451             },
27452            
27453             
27454             align : this.align,
27455             cn : [
27456                 img,
27457               
27458                 {
27459                     tag: 'figcaption',
27460                     'data-display' : this.caption_display,
27461                     style : {
27462                         textAlign : 'left',
27463                         fontSize : '16px',
27464                         lineHeight : '24px',
27465                         display : this.caption_display,
27466                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
27467                         margin: m,
27468                         width: this.align == 'center' ?  this.width : '100%' 
27469                     
27470                          
27471                     },
27472                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
27473                     cn : [
27474                         {
27475                             tag: 'div',
27476                             style  : {
27477                                 marginTop : '16px',
27478                                 textAlign : 'left'
27479                             },
27480                             align: 'left',
27481                             cn : [
27482                                 {
27483                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
27484                                     tag : 'i',
27485                                     contenteditable : true,
27486                                     html : captionhtml
27487                                 }
27488                                 
27489                             ]
27490                         }
27491                         
27492                     ]
27493                     
27494                 }
27495             ]
27496         };
27497         return ret;
27498          
27499     },
27500     
27501     readElement : function(node)
27502     {
27503         // this should not really come from the link...
27504         this.video_url = this.getVal(node, 'div', 'src');
27505         this.cls = this.getVal(node, 'div', 'class');
27506         this.href = this.getVal(node, 'a', 'href');
27507         
27508         
27509         this.image_src = this.getVal(node, 'img', 'src');
27510          
27511         this.align = this.getVal(node, 'figure', 'align');
27512         var figcaption = this.getVal(node, 'figcaption', false);
27513         if (figcaption !== '') {
27514             this.caption = this.getVal(figcaption, 'i', 'html');
27515         }
27516         
27517
27518         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
27519         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
27520         this.width = this.getVal(node, true, 'data-width');
27521         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
27522         
27523     },
27524     removeNode : function()
27525     {
27526         return this.node;
27527     }
27528     
27529   
27530    
27531      
27532     
27533     
27534     
27535     
27536 })
27537
27538  
27539
27540 /**
27541  * @class Roo.htmleditor.BlockTable
27542  * Block that manages a table
27543  * 
27544  * @constructor
27545  * Create a new Filter.
27546  * @param {Object} config Configuration options
27547  */
27548
27549 Roo.htmleditor.BlockTable = function(cfg)
27550 {
27551     if (cfg.node) {
27552         this.readElement(cfg.node);
27553         this.updateElement(cfg.node);
27554     }
27555     Roo.apply(this, cfg);
27556     if (!cfg.node) {
27557         this.rows = [];
27558         for(var r = 0; r < this.no_row; r++) {
27559             this.rows[r] = [];
27560             for(var c = 0; c < this.no_col; c++) {
27561                 this.rows[r][c] = this.emptyCell();
27562             }
27563         }
27564     }
27565     
27566     
27567 }
27568 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
27569  
27570     rows : false,
27571     no_col : 1,
27572     no_row : 1,
27573     
27574     
27575     width: '100%',
27576     
27577     // used by context menu
27578     friendly_name : 'Table',
27579     deleteTitle : 'Delete Table',
27580     // context menu is drawn once..
27581     
27582     contextMenu : function(toolbar)
27583     {
27584         
27585         var block = function() {
27586             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
27587         };
27588         
27589         
27590         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
27591         
27592         var syncValue = toolbar.editorcore.syncValue;
27593         
27594         var fields = {};
27595         
27596         return [
27597             {
27598                 xtype : 'TextItem',
27599                 text : "Width: ",
27600                 xns : rooui.Toolbar  //Boostrap?
27601             },
27602             {
27603                 xtype : 'ComboBox',
27604                 allowBlank : false,
27605                 displayField : 'val',
27606                 editable : true,
27607                 listWidth : 100,
27608                 triggerAction : 'all',
27609                 typeAhead : true,
27610                 valueField : 'val',
27611                 width : 100,
27612                 name : 'width',
27613                 listeners : {
27614                     select : function (combo, r, index)
27615                     {
27616                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27617                         var b = block();
27618                         b.width = r.get('val');
27619                         b.updateElement();
27620                         syncValue();
27621                         toolbar.editorcore.onEditorEvent();
27622                     }
27623                 },
27624                 xns : rooui.form,
27625                 store : {
27626                     xtype : 'SimpleStore',
27627                     data : [
27628                         ['100%'],
27629                         ['auto']
27630                     ],
27631                     fields : [ 'val'],
27632                     xns : Roo.data
27633                 }
27634             },
27635             // -------- Cols
27636             
27637             {
27638                 xtype : 'TextItem',
27639                 text : "Columns: ",
27640                 xns : rooui.Toolbar  //Boostrap?
27641             },
27642          
27643             {
27644                 xtype : 'Button',
27645                 text: '-',
27646                 listeners : {
27647                     click : function (_self, e)
27648                     {
27649                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27650                         block().removeColumn();
27651                         syncValue();
27652                         toolbar.editorcore.onEditorEvent();
27653                     }
27654                 },
27655                 xns : rooui.Toolbar
27656             },
27657             {
27658                 xtype : 'Button',
27659                 text: '+',
27660                 listeners : {
27661                     click : function (_self, e)
27662                     {
27663                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27664                         block().addColumn();
27665                         syncValue();
27666                         toolbar.editorcore.onEditorEvent();
27667                     }
27668                 },
27669                 xns : rooui.Toolbar
27670             },
27671             // -------- ROWS
27672             {
27673                 xtype : 'TextItem',
27674                 text : "Rows: ",
27675                 xns : rooui.Toolbar  //Boostrap?
27676             },
27677          
27678             {
27679                 xtype : 'Button',
27680                 text: '-',
27681                 listeners : {
27682                     click : function (_self, e)
27683                     {
27684                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27685                         block().removeRow();
27686                         syncValue();
27687                         toolbar.editorcore.onEditorEvent();
27688                     }
27689                 },
27690                 xns : rooui.Toolbar
27691             },
27692             {
27693                 xtype : 'Button',
27694                 text: '+',
27695                 listeners : {
27696                     click : function (_self, e)
27697                     {
27698                         block().addRow();
27699                         syncValue();
27700                         toolbar.editorcore.onEditorEvent();
27701                     }
27702                 },
27703                 xns : rooui.Toolbar
27704             },
27705             // -------- ROWS
27706             {
27707                 xtype : 'Button',
27708                 text: 'Reset Column Widths',
27709                 listeners : {
27710                     
27711                     click : function (_self, e)
27712                     {
27713                         block().resetWidths();
27714                         syncValue();
27715                         toolbar.editorcore.onEditorEvent();
27716                     }
27717                 },
27718                 xns : rooui.Toolbar
27719             } 
27720             
27721             
27722             
27723         ];
27724         
27725     },
27726     
27727     
27728   /**
27729      * create a DomHelper friendly object - for use with
27730      * Roo.DomHelper.markup / overwrite / etc..
27731      * ?? should it be called with option to hide all editing features?
27732      */
27733     toObject : function()
27734     {
27735         
27736         var ret = {
27737             tag : 'table',
27738             contenteditable : 'false', // this stops cell selection from picking the table.
27739             'data-block' : 'Table',
27740             style : {
27741                 width:  this.width,
27742                 border : 'solid 1px #000', // ??? hard coded?
27743                 'border-collapse' : 'collapse' 
27744             },
27745             cn : [
27746                 { tag : 'tbody' , cn : [] }
27747             ]
27748         };
27749         
27750         // do we have a head = not really 
27751         var ncols = 0;
27752         Roo.each(this.rows, function( row ) {
27753             var tr = {
27754                 tag: 'tr',
27755                 style : {
27756                     margin: '6px',
27757                     border : 'solid 1px #000',
27758                     textAlign : 'left' 
27759                 },
27760                 cn : [ ]
27761             };
27762             
27763             ret.cn[0].cn.push(tr);
27764             // does the row have any properties? ?? height?
27765             var nc = 0;
27766             Roo.each(row, function( cell ) {
27767                 
27768                 var td = {
27769                     tag : 'td',
27770                     contenteditable :  'true',
27771                     'data-block' : 'Td',
27772                     html : cell.html,
27773                     style : cell.style
27774                 };
27775                 if (cell.colspan > 1) {
27776                     td.colspan = cell.colspan ;
27777                     nc += cell.colspan;
27778                 } else {
27779                     nc++;
27780                 }
27781                 if (cell.rowspan > 1) {
27782                     td.rowspan = cell.rowspan ;
27783                 }
27784                 
27785                 
27786                 // widths ?
27787                 tr.cn.push(td);
27788                     
27789                 
27790             }, this);
27791             ncols = Math.max(nc, ncols);
27792             
27793             
27794         }, this);
27795         // add the header row..
27796         
27797         ncols++;
27798          
27799         
27800         return ret;
27801          
27802     },
27803     
27804     readElement : function(node)
27805     {
27806         node  = node ? node : this.node ;
27807         this.width = this.getVal(node, true, 'style', 'width') || '100%';
27808         
27809         this.rows = [];
27810         this.no_row = 0;
27811         var trs = Array.from(node.rows);
27812         trs.forEach(function(tr) {
27813             var row =  [];
27814             this.rows.push(row);
27815             
27816             this.no_row++;
27817             var no_column = 0;
27818             Array.from(tr.cells).forEach(function(td) {
27819                 
27820                 var add = {
27821                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
27822                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
27823                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
27824                     html : td.innerHTML
27825                 };
27826                 no_column += add.colspan;
27827                      
27828                 
27829                 row.push(add);
27830                 
27831                 
27832             },this);
27833             this.no_col = Math.max(this.no_col, no_column);
27834             
27835             
27836         },this);
27837         
27838         
27839     },
27840     normalizeRows: function()
27841     {
27842         var ret= [];
27843         var rid = -1;
27844         this.rows.forEach(function(row) {
27845             rid++;
27846             ret[rid] = [];
27847             row = this.normalizeRow(row);
27848             var cid = 0;
27849             row.forEach(function(c) {
27850                 while (typeof(ret[rid][cid]) != 'undefined') {
27851                     cid++;
27852                 }
27853                 if (typeof(ret[rid]) == 'undefined') {
27854                     ret[rid] = [];
27855                 }
27856                 ret[rid][cid] = c;
27857                 c.row = rid;
27858                 c.col = cid;
27859                 if (c.rowspan < 2) {
27860                     return;
27861                 }
27862                 
27863                 for(var i = 1 ;i < c.rowspan; i++) {
27864                     if (typeof(ret[rid+i]) == 'undefined') {
27865                         ret[rid+i] = [];
27866                     }
27867                     ret[rid+i][cid] = c;
27868                 }
27869             });
27870         }, this);
27871         return ret;
27872     
27873     },
27874     
27875     normalizeRow: function(row)
27876     {
27877         var ret= [];
27878         row.forEach(function(c) {
27879             if (c.colspan < 2) {
27880                 ret.push(c);
27881                 return;
27882             }
27883             for(var i =0 ;i < c.colspan; i++) {
27884                 ret.push(c);
27885             }
27886         });
27887         return ret;
27888     
27889     },
27890     
27891     deleteColumn : function(sel)
27892     {
27893         if (!sel || sel.type != 'col') {
27894             return;
27895         }
27896         if (this.no_col < 2) {
27897             return;
27898         }
27899         
27900         this.rows.forEach(function(row) {
27901             var cols = this.normalizeRow(row);
27902             var col = cols[sel.col];
27903             if (col.colspan > 1) {
27904                 col.colspan --;
27905             } else {
27906                 row.remove(col);
27907             }
27908             
27909         }, this);
27910         this.no_col--;
27911         
27912     },
27913     removeColumn : function()
27914     {
27915         this.deleteColumn({
27916             type: 'col',
27917             col : this.no_col-1
27918         });
27919         this.updateElement();
27920     },
27921     
27922      
27923     addColumn : function()
27924     {
27925         
27926         this.rows.forEach(function(row) {
27927             row.push(this.emptyCell());
27928            
27929         }, this);
27930         this.updateElement();
27931     },
27932     
27933     deleteRow : function(sel)
27934     {
27935         if (!sel || sel.type != 'row') {
27936             return;
27937         }
27938         
27939         if (this.no_row < 2) {
27940             return;
27941         }
27942         
27943         var rows = this.normalizeRows();
27944         
27945         
27946         rows[sel.row].forEach(function(col) {
27947             if (col.rowspan > 1) {
27948                 col.rowspan--;
27949             } else {
27950                 col.remove = 1; // flage it as removed.
27951             }
27952             
27953         }, this);
27954         var newrows = [];
27955         this.rows.forEach(function(row) {
27956             newrow = [];
27957             row.forEach(function(c) {
27958                 if (typeof(c.remove) == 'undefined') {
27959                     newrow.push(c);
27960                 }
27961                 
27962             });
27963             if (newrow.length > 0) {
27964                 newrows.push(row);
27965             }
27966         });
27967         this.rows =  newrows;
27968         
27969         
27970         
27971         this.no_row--;
27972         this.updateElement();
27973         
27974     },
27975     removeRow : function()
27976     {
27977         this.deleteRow({
27978             type: 'row',
27979             row : this.no_row-1
27980         });
27981         
27982     },
27983     
27984      
27985     addRow : function()
27986     {
27987         
27988         var row = [];
27989         for (var i = 0; i < this.no_col; i++ ) {
27990             
27991             row.push(this.emptyCell());
27992            
27993         }
27994         this.rows.push(row);
27995         this.updateElement();
27996         
27997     },
27998      
27999     // the default cell object... at present...
28000     emptyCell : function() {
28001         return (new Roo.htmleditor.BlockTd({})).toObject();
28002         
28003      
28004     },
28005     
28006     removeNode : function()
28007     {
28008         return this.node;
28009     },
28010     
28011     
28012     
28013     resetWidths : function()
28014     {
28015         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
28016             var nn = Roo.htmleditor.Block.factory(n);
28017             nn.width = '';
28018             nn.updateElement(n);
28019         });
28020     }
28021     
28022     
28023     
28024     
28025 })
28026
28027 /**
28028  *
28029  * editing a TD?
28030  *
28031  * since selections really work on the table cell, then editing really should work from there
28032  *
28033  * The original plan was to support merging etc... - but that may not be needed yet..
28034  *
28035  * So this simple version will support:
28036  *   add/remove cols
28037  *   adjust the width +/-
28038  *   reset the width...
28039  *   
28040  *
28041  */
28042
28043
28044  
28045
28046 /**
28047  * @class Roo.htmleditor.BlockTable
28048  * Block that manages a table
28049  * 
28050  * @constructor
28051  * Create a new Filter.
28052  * @param {Object} config Configuration options
28053  */
28054
28055 Roo.htmleditor.BlockTd = function(cfg)
28056 {
28057     if (cfg.node) {
28058         this.readElement(cfg.node);
28059         this.updateElement(cfg.node);
28060     }
28061     Roo.apply(this, cfg);
28062      
28063     
28064     
28065 }
28066 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
28067  
28068     node : false,
28069     
28070     width: '',
28071     textAlign : 'left',
28072     valign : 'top',
28073     
28074     colspan : 1,
28075     rowspan : 1,
28076     
28077     
28078     // used by context menu
28079     friendly_name : 'Table Cell',
28080     deleteTitle : false, // use our customer delete
28081     
28082     // context menu is drawn once..
28083     
28084     contextMenu : function(toolbar)
28085     {
28086         
28087         var cell = function() {
28088             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
28089         };
28090         
28091         var table = function() {
28092             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
28093         };
28094         
28095         var lr = false;
28096         var saveSel = function()
28097         {
28098             lr = toolbar.editorcore.getSelection().getRangeAt(0);
28099         }
28100         var restoreSel = function()
28101         {
28102             if (lr) {
28103                 (function() {
28104                     toolbar.editorcore.focus();
28105                     var cr = toolbar.editorcore.getSelection();
28106                     cr.removeAllRanges();
28107                     cr.addRange(lr);
28108                     toolbar.editorcore.onEditorEvent();
28109                 }).defer(10, this);
28110                 
28111                 
28112             }
28113         }
28114         
28115         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
28116         
28117         var syncValue = toolbar.editorcore.syncValue;
28118         
28119         var fields = {};
28120         
28121         return [
28122             {
28123                 xtype : 'Button',
28124                 text : 'Edit Table',
28125                 listeners : {
28126                     click : function() {
28127                         var t = toolbar.tb.selectedNode.closest('table');
28128                         toolbar.editorcore.selectNode(t);
28129                         toolbar.editorcore.onEditorEvent();                        
28130                     }
28131                 }
28132                 
28133             },
28134               
28135            
28136              
28137             {
28138                 xtype : 'TextItem',
28139                 text : "Column Width: ",
28140                  xns : rooui.Toolbar 
28141                
28142             },
28143             {
28144                 xtype : 'Button',
28145                 text: '-',
28146                 listeners : {
28147                     click : function (_self, e)
28148                     {
28149                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28150                         cell().shrinkColumn();
28151                         syncValue();
28152                          toolbar.editorcore.onEditorEvent();
28153                     }
28154                 },
28155                 xns : rooui.Toolbar
28156             },
28157             {
28158                 xtype : 'Button',
28159                 text: '+',
28160                 listeners : {
28161                     click : function (_self, e)
28162                     {
28163                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28164                         cell().growColumn();
28165                         syncValue();
28166                         toolbar.editorcore.onEditorEvent();
28167                     }
28168                 },
28169                 xns : rooui.Toolbar
28170             },
28171             
28172             {
28173                 xtype : 'TextItem',
28174                 text : "Vertical Align: ",
28175                 xns : rooui.Toolbar  //Boostrap?
28176             },
28177             {
28178                 xtype : 'ComboBox',
28179                 allowBlank : false,
28180                 displayField : 'val',
28181                 editable : true,
28182                 listWidth : 100,
28183                 triggerAction : 'all',
28184                 typeAhead : true,
28185                 valueField : 'val',
28186                 width : 100,
28187                 name : 'valign',
28188                 listeners : {
28189                     select : function (combo, r, index)
28190                     {
28191                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28192                         var b = cell();
28193                         b.valign = r.get('val');
28194                         b.updateElement();
28195                         syncValue();
28196                         toolbar.editorcore.onEditorEvent();
28197                     }
28198                 },
28199                 xns : rooui.form,
28200                 store : {
28201                     xtype : 'SimpleStore',
28202                     data : [
28203                         ['top'],
28204                         ['middle'],
28205                         ['bottom'] // there are afew more... 
28206                     ],
28207                     fields : [ 'val'],
28208                     xns : Roo.data
28209                 }
28210             },
28211             
28212             {
28213                 xtype : 'TextItem',
28214                 text : "Merge Cells: ",
28215                  xns : rooui.Toolbar 
28216                
28217             },
28218             
28219             
28220             {
28221                 xtype : 'Button',
28222                 text: 'Right',
28223                 listeners : {
28224                     click : function (_self, e)
28225                     {
28226                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28227                         cell().mergeRight();
28228                         //block().growColumn();
28229                         syncValue();
28230                         toolbar.editorcore.onEditorEvent();
28231                     }
28232                 },
28233                 xns : rooui.Toolbar
28234             },
28235              
28236             {
28237                 xtype : 'Button',
28238                 text: 'Below',
28239                 listeners : {
28240                     click : function (_self, e)
28241                     {
28242                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28243                         cell().mergeBelow();
28244                         //block().growColumn();
28245                         syncValue();
28246                         toolbar.editorcore.onEditorEvent();
28247                     }
28248                 },
28249                 xns : rooui.Toolbar
28250             },
28251             {
28252                 xtype : 'TextItem',
28253                 text : "| ",
28254                  xns : rooui.Toolbar 
28255                
28256             },
28257             
28258             {
28259                 xtype : 'Button',
28260                 text: 'Split',
28261                 listeners : {
28262                     click : function (_self, e)
28263                     {
28264                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28265                         cell().split();
28266                         syncValue();
28267                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28268                         toolbar.editorcore.onEditorEvent();
28269                                              
28270                     }
28271                 },
28272                 xns : rooui.Toolbar
28273             },
28274             {
28275                 xtype : 'Fill',
28276                 xns : rooui.Toolbar 
28277                
28278             },
28279         
28280           
28281             {
28282                 xtype : 'Button',
28283                 text: 'Delete',
28284                  
28285                 xns : rooui.Toolbar,
28286                 menu : {
28287                     xtype : 'Menu',
28288                     xns : rooui.menu,
28289                     items : [
28290                         {
28291                             xtype : 'Item',
28292                             html: 'Column',
28293                             listeners : {
28294                                 click : function (_self, e)
28295                                 {
28296                                     var t = table();
28297                                     
28298                                     cell().deleteColumn();
28299                                     syncValue();
28300                                     toolbar.editorcore.selectNode(t.node);
28301                                     toolbar.editorcore.onEditorEvent();   
28302                                 }
28303                             },
28304                             xns : rooui.menu
28305                         },
28306                         {
28307                             xtype : 'Item',
28308                             html: 'Row',
28309                             listeners : {
28310                                 click : function (_self, e)
28311                                 {
28312                                     var t = table();
28313                                     cell().deleteRow();
28314                                     syncValue();
28315                                     
28316                                     toolbar.editorcore.selectNode(t.node);
28317                                     toolbar.editorcore.onEditorEvent();   
28318                                                          
28319                                 }
28320                             },
28321                             xns : rooui.menu
28322                         },
28323                        {
28324                             xtype : 'Separator',
28325                             xns : rooui.menu
28326                         },
28327                         {
28328                             xtype : 'Item',
28329                             html: 'Table',
28330                             listeners : {
28331                                 click : function (_self, e)
28332                                 {
28333                                     var t = table();
28334                                     var nn = t.node.nextSibling || t.node.previousSibling;
28335                                     t.node.parentNode.removeChild(t.node);
28336                                     if (nn) { 
28337                                         toolbar.editorcore.selectNode(nn, true);
28338                                     }
28339                                     toolbar.editorcore.onEditorEvent();   
28340                                                          
28341                                 }
28342                             },
28343                             xns : rooui.menu
28344                         }
28345                     ]
28346                 }
28347             }
28348             
28349             // align... << fixme
28350             
28351         ];
28352         
28353     },
28354     
28355     
28356   /**
28357      * create a DomHelper friendly object - for use with
28358      * Roo.DomHelper.markup / overwrite / etc..
28359      * ?? should it be called with option to hide all editing features?
28360      */
28361  /**
28362      * create a DomHelper friendly object - for use with
28363      * Roo.DomHelper.markup / overwrite / etc..
28364      * ?? should it be called with option to hide all editing features?
28365      */
28366     toObject : function()
28367     {
28368         
28369         var ret = {
28370             tag : 'td',
28371             contenteditable : 'true', // this stops cell selection from picking the table.
28372             'data-block' : 'Td',
28373             valign : this.valign,
28374             style : {  
28375                 'text-align' :  this.textAlign,
28376                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
28377                 'border-collapse' : 'collapse',
28378                 padding : '6px', // 8 for desktop / 4 for mobile
28379                 'vertical-align': this.valign
28380             },
28381             html : this.html
28382         };
28383         if (this.width != '') {
28384             ret.width = this.width;
28385             ret.style.width = this.width;
28386         }
28387         
28388         
28389         if (this.colspan > 1) {
28390             ret.colspan = this.colspan ;
28391         } 
28392         if (this.rowspan > 1) {
28393             ret.rowspan = this.rowspan ;
28394         }
28395         
28396            
28397         
28398         return ret;
28399          
28400     },
28401     
28402     readElement : function(node)
28403     {
28404         node  = node ? node : this.node ;
28405         this.width = node.style.width;
28406         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
28407         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
28408         this.html = node.innerHTML;
28409         
28410         
28411     },
28412      
28413     // the default cell object... at present...
28414     emptyCell : function() {
28415         return {
28416             colspan :  1,
28417             rowspan :  1,
28418             textAlign : 'left',
28419             html : "&nbsp;" // is this going to be editable now?
28420         };
28421      
28422     },
28423     
28424     removeNode : function()
28425     {
28426         return this.node.closest('table');
28427          
28428     },
28429     
28430     cellData : false,
28431     
28432     colWidths : false,
28433     
28434     toTableArray  : function()
28435     {
28436         var ret = [];
28437         var tab = this.node.closest('tr').closest('table');
28438         Array.from(tab.rows).forEach(function(r, ri){
28439             ret[ri] = [];
28440         });
28441         var rn = 0;
28442         this.colWidths = [];
28443         var all_auto = true;
28444         Array.from(tab.rows).forEach(function(r, ri){
28445             
28446             var cn = 0;
28447             Array.from(r.cells).forEach(function(ce, ci){
28448                 var c =  {
28449                     cell : ce,
28450                     row : rn,
28451                     col: cn,
28452                     colspan : ce.colSpan,
28453                     rowspan : ce.rowSpan
28454                 };
28455                 if (ce.isEqualNode(this.node)) {
28456                     this.cellData = c;
28457                 }
28458                 // if we have been filled up by a row?
28459                 if (typeof(ret[rn][cn]) != 'undefined') {
28460                     while(typeof(ret[rn][cn]) != 'undefined') {
28461                         cn++;
28462                     }
28463                     c.col = cn;
28464                 }
28465                 
28466                 if (typeof(this.colWidths[cn]) == 'undefined') {
28467                     this.colWidths[cn] =   ce.style.width;
28468                     if (this.colWidths[cn] != '') {
28469                         all_auto = false;
28470                     }
28471                 }
28472                 
28473                 
28474                 if (c.colspan < 2 && c.rowspan < 2 ) {
28475                     ret[rn][cn] = c;
28476                     cn++;
28477                     return;
28478                 }
28479                 for(var j = 0; j < c.rowspan; j++) {
28480                     if (typeof(ret[rn+j]) == 'undefined') {
28481                         continue; // we have a problem..
28482                     }
28483                     ret[rn+j][cn] = c;
28484                     for(var i = 0; i < c.colspan; i++) {
28485                         ret[rn+j][cn+i] = c;
28486                     }
28487                 }
28488                 
28489                 cn += c.colspan;
28490             }, this);
28491             rn++;
28492         }, this);
28493         
28494         // initalize widths.?
28495         // either all widths or no widths..
28496         if (all_auto) {
28497             this.colWidths[0] = false; // no widths flag.
28498         }
28499         
28500         
28501         return ret;
28502         
28503     },
28504     
28505     
28506     
28507     
28508     mergeRight: function()
28509     {
28510          
28511         // get the contents of the next cell along..
28512         var tr = this.node.closest('tr');
28513         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
28514         if (i >= tr.childNodes.length - 1) {
28515             return; // no cells on right to merge with.
28516         }
28517         var table = this.toTableArray();
28518         
28519         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
28520             return; // nothing right?
28521         }
28522         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
28523         // right cell - must be same rowspan and on the same row.
28524         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
28525             return; // right hand side is not same rowspan.
28526         }
28527         
28528         
28529         
28530         this.node.innerHTML += ' ' + rc.cell.innerHTML;
28531         tr.removeChild(rc.cell);
28532         this.colspan += rc.colspan;
28533         this.node.setAttribute('colspan', this.colspan);
28534
28535     },
28536     
28537     
28538     mergeBelow : function()
28539     {
28540         var table = this.toTableArray();
28541         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
28542             return; // no row below
28543         }
28544         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
28545             return; // nothing right?
28546         }
28547         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
28548         
28549         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
28550             return; // right hand side is not same rowspan.
28551         }
28552         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
28553         rc.cell.parentNode.removeChild(rc.cell);
28554         this.rowspan += rc.rowspan;
28555         this.node.setAttribute('rowspan', this.rowspan);
28556     },
28557     
28558     split: function()
28559     {
28560         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
28561             return;
28562         }
28563         var table = this.toTableArray();
28564         var cd = this.cellData;
28565         this.rowspan = 1;
28566         this.colspan = 1;
28567         
28568         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
28569             
28570             
28571             
28572             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
28573                 if (r == cd.row && c == cd.col) {
28574                     this.node.removeAttribute('rowspan');
28575                     this.node.removeAttribute('colspan');
28576                     continue;
28577                 }
28578                  
28579                 var ntd = this.node.cloneNode(); // which col/row should be 0..
28580                 ntd.removeAttribute('id'); //
28581                 //ntd.style.width  = '';
28582                 ntd.innerHTML = '';
28583                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
28584             }
28585             
28586         }
28587         this.redrawAllCells(table);
28588         
28589          
28590         
28591     },
28592     
28593     
28594     
28595     redrawAllCells: function(table)
28596     {
28597         
28598          
28599         var tab = this.node.closest('tr').closest('table');
28600         var ctr = tab.rows[0].parentNode;
28601         Array.from(tab.rows).forEach(function(r, ri){
28602             
28603             Array.from(r.cells).forEach(function(ce, ci){
28604                 ce.parentNode.removeChild(ce);
28605             });
28606             r.parentNode.removeChild(r);
28607         });
28608         for(var r = 0 ; r < table.length; r++) {
28609             var re = tab.rows[r];
28610             
28611             var re = tab.ownerDocument.createElement('tr');
28612             ctr.appendChild(re);
28613             for(var c = 0 ; c < table[r].length; c++) {
28614                 if (table[r][c].cell === false) {
28615                     continue;
28616                 }
28617                 
28618                 re.appendChild(table[r][c].cell);
28619                  
28620                 table[r][c].cell = false;
28621             }
28622         }
28623         
28624     },
28625     updateWidths : function(table)
28626     {
28627         for(var r = 0 ; r < table.length; r++) {
28628            
28629             for(var c = 0 ; c < table[r].length; c++) {
28630                 if (table[r][c].cell === false) {
28631                     continue;
28632                 }
28633                 
28634                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
28635                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
28636                     el.width = Math.floor(this.colWidths[c])  +'%';
28637                     el.updateElement(el.node);
28638                 }
28639                 table[r][c].cell = false; // done
28640             }
28641         }
28642     },
28643     normalizeWidths : function(table)
28644     {
28645     
28646         if (this.colWidths[0] === false) {
28647             var nw = 100.0 / this.colWidths.length;
28648             this.colWidths.forEach(function(w,i) {
28649                 this.colWidths[i] = nw;
28650             },this);
28651             return;
28652         }
28653     
28654         var t = 0, missing = [];
28655         
28656         this.colWidths.forEach(function(w,i) {
28657             //if you mix % and
28658             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
28659             var add =  this.colWidths[i];
28660             if (add > 0) {
28661                 t+=add;
28662                 return;
28663             }
28664             missing.push(i);
28665             
28666             
28667         },this);
28668         var nc = this.colWidths.length;
28669         if (missing.length) {
28670             var mult = (nc - missing.length) / (1.0 * nc);
28671             var t = mult * t;
28672             var ew = (100 -t) / (1.0 * missing.length);
28673             this.colWidths.forEach(function(w,i) {
28674                 if (w > 0) {
28675                     this.colWidths[i] = w * mult;
28676                     return;
28677                 }
28678                 
28679                 this.colWidths[i] = ew;
28680             }, this);
28681             // have to make up numbers..
28682              
28683         }
28684         // now we should have all the widths..
28685         
28686     
28687     },
28688     
28689     shrinkColumn : function()
28690     {
28691         var table = this.toTableArray();
28692         this.normalizeWidths(table);
28693         var col = this.cellData.col;
28694         var nw = this.colWidths[col] * 0.8;
28695         if (nw < 5) {
28696             return;
28697         }
28698         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
28699         this.colWidths.forEach(function(w,i) {
28700             if (i == col) {
28701                  this.colWidths[i] = nw;
28702                 return;
28703             }
28704             this.colWidths[i] += otherAdd
28705         }, this);
28706         this.updateWidths(table);
28707          
28708     },
28709     growColumn : function()
28710     {
28711         var table = this.toTableArray();
28712         this.normalizeWidths(table);
28713         var col = this.cellData.col;
28714         var nw = this.colWidths[col] * 1.2;
28715         if (nw > 90) {
28716             return;
28717         }
28718         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
28719         this.colWidths.forEach(function(w,i) {
28720             if (i == col) {
28721                 this.colWidths[i] = nw;
28722                 return;
28723             }
28724             this.colWidths[i] -= otherSub
28725         }, this);
28726         this.updateWidths(table);
28727          
28728     },
28729     deleteRow : function()
28730     {
28731         // delete this rows 'tr'
28732         // if any of the cells in this row have a rowspan > 1 && row!= this row..
28733         // then reduce the rowspan.
28734         var table = this.toTableArray();
28735         // this.cellData.row;
28736         for (var i =0;i< table[this.cellData.row].length ; i++) {
28737             var c = table[this.cellData.row][i];
28738             if (c.row != this.cellData.row) {
28739                 
28740                 c.rowspan--;
28741                 c.cell.setAttribute('rowspan', c.rowspan);
28742                 continue;
28743             }
28744             if (c.rowspan > 1) {
28745                 c.rowspan--;
28746                 c.cell.setAttribute('rowspan', c.rowspan);
28747             }
28748         }
28749         table.splice(this.cellData.row,1);
28750         this.redrawAllCells(table);
28751         
28752     },
28753     deleteColumn : function()
28754     {
28755         var table = this.toTableArray();
28756         
28757         for (var i =0;i< table.length ; i++) {
28758             var c = table[i][this.cellData.col];
28759             if (c.col != this.cellData.col) {
28760                 table[i][this.cellData.col].colspan--;
28761             } else if (c.colspan > 1) {
28762                 c.colspan--;
28763                 c.cell.setAttribute('colspan', c.colspan);
28764             }
28765             table[i].splice(this.cellData.col,1);
28766         }
28767         
28768         this.redrawAllCells(table);
28769     }
28770     
28771     
28772     
28773     
28774 })
28775
28776 //<script type="text/javascript">
28777
28778 /*
28779  * Based  Ext JS Library 1.1.1
28780  * Copyright(c) 2006-2007, Ext JS, LLC.
28781  * LGPL
28782  *
28783  */
28784  
28785 /**
28786  * @class Roo.HtmlEditorCore
28787  * @extends Roo.Component
28788  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
28789  *
28790  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
28791  */
28792
28793 Roo.HtmlEditorCore = function(config){
28794     
28795     
28796     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
28797     
28798     
28799     this.addEvents({
28800         /**
28801          * @event initialize
28802          * Fires when the editor is fully initialized (including the iframe)
28803          * @param {Roo.HtmlEditorCore} this
28804          */
28805         initialize: true,
28806         /**
28807          * @event activate
28808          * Fires when the editor is first receives the focus. Any insertion must wait
28809          * until after this event.
28810          * @param {Roo.HtmlEditorCore} this
28811          */
28812         activate: true,
28813          /**
28814          * @event beforesync
28815          * Fires before the textarea is updated with content from the editor iframe. Return false
28816          * to cancel the sync.
28817          * @param {Roo.HtmlEditorCore} this
28818          * @param {String} html
28819          */
28820         beforesync: true,
28821          /**
28822          * @event beforepush
28823          * Fires before the iframe editor is updated with content from the textarea. Return false
28824          * to cancel the push.
28825          * @param {Roo.HtmlEditorCore} this
28826          * @param {String} html
28827          */
28828         beforepush: true,
28829          /**
28830          * @event sync
28831          * Fires when the textarea is updated with content from the editor iframe.
28832          * @param {Roo.HtmlEditorCore} this
28833          * @param {String} html
28834          */
28835         sync: true,
28836          /**
28837          * @event push
28838          * Fires when the iframe editor is updated with content from the textarea.
28839          * @param {Roo.HtmlEditorCore} this
28840          * @param {String} html
28841          */
28842         push: true,
28843         
28844         /**
28845          * @event editorevent
28846          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
28847          * @param {Roo.HtmlEditorCore} this
28848          */
28849         editorevent: true 
28850          
28851         
28852     });
28853     
28854     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
28855     
28856     // defaults : white / black...
28857     this.applyBlacklists();
28858     
28859     
28860     
28861 };
28862
28863
28864 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
28865
28866
28867      /**
28868      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
28869      */
28870     
28871     owner : false,
28872     
28873      /**
28874      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
28875      *                        Roo.resizable.
28876      */
28877     resizable : false,
28878      /**
28879      * @cfg {Number} height (in pixels)
28880      */   
28881     height: 300,
28882    /**
28883      * @cfg {Number} width (in pixels)
28884      */   
28885     width: 500,
28886      /**
28887      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
28888      *         if you are doing an email editor, this probably needs disabling, it's designed
28889      */
28890     autoClean: true,
28891     
28892     /**
28893      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
28894      */
28895     enableBlocks : true,
28896     /**
28897      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
28898      * 
28899      */
28900     stylesheets: false,
28901      /**
28902      * @cfg {String} language default en - language of text (usefull for rtl languages)
28903      * 
28904      */
28905     language: 'en',
28906     
28907     /**
28908      * @cfg {boolean} allowComments - default false - allow comments in HTML source
28909      *          - by default they are stripped - if you are editing email you may need this.
28910      */
28911     allowComments: false,
28912     // id of frame..
28913     frameId: false,
28914     
28915     // private properties
28916     validationEvent : false,
28917     deferHeight: true,
28918     initialized : false,
28919     activated : false,
28920     sourceEditMode : false,
28921     onFocus : Roo.emptyFn,
28922     iframePad:3,
28923     hideMode:'offsets',
28924     
28925     clearUp: true,
28926     
28927     // blacklist + whitelisted elements..
28928     black: false,
28929     white: false,
28930      
28931     bodyCls : '',
28932
28933     
28934     undoManager : false,
28935     /**
28936      * Protected method that will not generally be called directly. It
28937      * is called when the editor initializes the iframe with HTML contents. Override this method if you
28938      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
28939      */
28940     getDocMarkup : function(){
28941         // body styles..
28942         var st = '';
28943         
28944         // inherit styels from page...?? 
28945         if (this.stylesheets === false) {
28946             
28947             Roo.get(document.head).select('style').each(function(node) {
28948                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
28949             });
28950             
28951             Roo.get(document.head).select('link').each(function(node) { 
28952                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
28953             });
28954             
28955         } else if (!this.stylesheets.length) {
28956                 // simple..
28957                 st = '<style type="text/css">' +
28958                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
28959                    '</style>';
28960         } else {
28961             for (var i in this.stylesheets) {
28962                 if (typeof(this.stylesheets[i]) != 'string') {
28963                     continue;
28964                 }
28965                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
28966             }
28967             
28968         }
28969         
28970         st +=  '<style type="text/css">' +
28971             'IMG { cursor: pointer } ' +
28972         '</style>';
28973         
28974         st += '<meta name="google" content="notranslate">';
28975         
28976         var cls = 'notranslate roo-htmleditor-body';
28977         
28978         if(this.bodyCls.length){
28979             cls += ' ' + this.bodyCls;
28980         }
28981         
28982         return '<html  class="notranslate" translate="no"><head>' + st  +
28983             //<style type="text/css">' +
28984             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
28985             //'</style>' +
28986             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
28987     },
28988
28989     // private
28990     onRender : function(ct, position)
28991     {
28992         var _t = this;
28993         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
28994         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
28995         
28996         
28997         this.el.dom.style.border = '0 none';
28998         this.el.dom.setAttribute('tabIndex', -1);
28999         this.el.addClass('x-hidden hide');
29000         
29001         
29002         
29003         if(Roo.isIE){ // fix IE 1px bogus margin
29004             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
29005         }
29006        
29007         
29008         this.frameId = Roo.id();
29009         
29010          
29011         
29012         var iframe = this.owner.wrap.createChild({
29013             tag: 'iframe',
29014             cls: 'form-control', // bootstrap..
29015             id: this.frameId,
29016             name: this.frameId,
29017             frameBorder : 'no',
29018             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
29019         }, this.el
29020         );
29021         
29022         
29023         this.iframe = iframe.dom;
29024
29025         this.assignDocWin();
29026         
29027         this.doc.designMode = 'on';
29028        
29029         this.doc.open();
29030         this.doc.write(this.getDocMarkup());
29031         this.doc.close();
29032
29033         
29034         var task = { // must defer to wait for browser to be ready
29035             run : function(){
29036                 //console.log("run task?" + this.doc.readyState);
29037                 this.assignDocWin();
29038                 if(this.doc.body || this.doc.readyState == 'complete'){
29039                     try {
29040                         this.doc.designMode="on";
29041                         
29042                     } catch (e) {
29043                         return;
29044                     }
29045                     Roo.TaskMgr.stop(task);
29046                     this.initEditor.defer(10, this);
29047                 }
29048             },
29049             interval : 10,
29050             duration: 10000,
29051             scope: this
29052         };
29053         Roo.TaskMgr.start(task);
29054
29055     },
29056
29057     // private
29058     onResize : function(w, h)
29059     {
29060          Roo.log('resize: ' +w + ',' + h );
29061         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
29062         if(!this.iframe){
29063             return;
29064         }
29065         if(typeof w == 'number'){
29066             
29067             this.iframe.style.width = w + 'px';
29068         }
29069         if(typeof h == 'number'){
29070             
29071             this.iframe.style.height = h + 'px';
29072             if(this.doc){
29073                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
29074             }
29075         }
29076         
29077     },
29078
29079     /**
29080      * Toggles the editor between standard and source edit mode.
29081      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
29082      */
29083     toggleSourceEdit : function(sourceEditMode){
29084         
29085         this.sourceEditMode = sourceEditMode === true;
29086         
29087         if(this.sourceEditMode){
29088  
29089             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
29090             
29091         }else{
29092             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
29093             //this.iframe.className = '';
29094             this.deferFocus();
29095         }
29096         //this.setSize(this.owner.wrap.getSize());
29097         //this.fireEvent('editmodechange', this, this.sourceEditMode);
29098     },
29099
29100     
29101   
29102
29103     /**
29104      * Protected method that will not generally be called directly. If you need/want
29105      * custom HTML cleanup, this is the method you should override.
29106      * @param {String} html The HTML to be cleaned
29107      * return {String} The cleaned HTML
29108      */
29109     cleanHtml : function(html)
29110     {
29111         html = String(html);
29112         if(html.length > 5){
29113             if(Roo.isSafari){ // strip safari nonsense
29114                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
29115             }
29116         }
29117         if(html == '&nbsp;'){
29118             html = '';
29119         }
29120         return html;
29121     },
29122
29123     /**
29124      * HTML Editor -> Textarea
29125      * Protected method that will not generally be called directly. Syncs the contents
29126      * of the editor iframe with the textarea.
29127      */
29128     syncValue : function()
29129     {
29130         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
29131         if(this.initialized){
29132             
29133             if (this.undoManager) {
29134                 this.undoManager.addEvent();
29135             }
29136
29137             
29138             var bd = (this.doc.body || this.doc.documentElement);
29139            
29140             
29141             var sel = this.win.getSelection();
29142             
29143             var div = document.createElement('div');
29144             div.innerHTML = bd.innerHTML;
29145             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
29146             if (gtx.length > 0) {
29147                 var rm = gtx.item(0).parentNode;
29148                 rm.parentNode.removeChild(rm);
29149             }
29150             
29151            
29152             if (this.enableBlocks) {
29153                 new Roo.htmleditor.FilterBlock({ node : div });
29154             }
29155             //?? tidy?
29156             var tidy = new Roo.htmleditor.TidySerializer({
29157                 inner:  true
29158             });
29159             var html  = tidy.serialize(div);
29160             
29161             
29162             if(Roo.isSafari){
29163                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
29164                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
29165                 if(m && m[1]){
29166                     html = '<div style="'+m[0]+'">' + html + '</div>';
29167                 }
29168             }
29169             html = this.cleanHtml(html);
29170             // fix up the special chars.. normaly like back quotes in word...
29171             // however we do not want to do this with chinese..
29172             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
29173                 
29174                 var cc = match.charCodeAt();
29175
29176                 // Get the character value, handling surrogate pairs
29177                 if (match.length == 2) {
29178                     // It's a surrogate pair, calculate the Unicode code point
29179                     var high = match.charCodeAt(0) - 0xD800;
29180                     var low  = match.charCodeAt(1) - 0xDC00;
29181                     cc = (high * 0x400) + low + 0x10000;
29182                 }  else if (
29183                     (cc >= 0x4E00 && cc < 0xA000 ) ||
29184                     (cc >= 0x3400 && cc < 0x4E00 ) ||
29185                     (cc >= 0xf900 && cc < 0xfb00 )
29186                 ) {
29187                         return match;
29188                 }  
29189          
29190                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
29191                 return "&#" + cc + ";";
29192                 
29193                 
29194             });
29195             
29196             
29197              
29198             if(this.owner.fireEvent('beforesync', this, html) !== false){
29199                 this.el.dom.value = html;
29200                 this.owner.fireEvent('sync', this, html);
29201             }
29202         }
29203     },
29204
29205     /**
29206      * TEXTAREA -> EDITABLE
29207      * Protected method that will not generally be called directly. Pushes the value of the textarea
29208      * into the iframe editor.
29209      */
29210     pushValue : function()
29211     {
29212         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
29213         if(this.initialized){
29214             var v = this.el.dom.value.trim();
29215             
29216             
29217             if(this.owner.fireEvent('beforepush', this, v) !== false){
29218                 var d = (this.doc.body || this.doc.documentElement);
29219                 d.innerHTML = v;
29220                  
29221                 this.el.dom.value = d.innerHTML;
29222                 this.owner.fireEvent('push', this, v);
29223             }
29224             if (this.autoClean) {
29225                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
29226                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
29227             }
29228             if (this.enableBlocks) {
29229                 Roo.htmleditor.Block.initAll(this.doc.body);
29230             }
29231             
29232             this.updateLanguage();
29233             
29234             var lc = this.doc.body.lastChild;
29235             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
29236                 // add an extra line at the end.
29237                 this.doc.body.appendChild(this.doc.createElement('br'));
29238             }
29239             
29240             
29241         }
29242     },
29243
29244     // private
29245     deferFocus : function(){
29246         this.focus.defer(10, this);
29247     },
29248
29249     // doc'ed in Field
29250     focus : function(){
29251         if(this.win && !this.sourceEditMode){
29252             this.win.focus();
29253         }else{
29254             this.el.focus();
29255         }
29256     },
29257     
29258     assignDocWin: function()
29259     {
29260         var iframe = this.iframe;
29261         
29262          if(Roo.isIE){
29263             this.doc = iframe.contentWindow.document;
29264             this.win = iframe.contentWindow;
29265         } else {
29266 //            if (!Roo.get(this.frameId)) {
29267 //                return;
29268 //            }
29269 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
29270 //            this.win = Roo.get(this.frameId).dom.contentWindow;
29271             
29272             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
29273                 return;
29274             }
29275             
29276             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
29277             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
29278         }
29279     },
29280     
29281     // private
29282     initEditor : function(){
29283         //console.log("INIT EDITOR");
29284         this.assignDocWin();
29285         
29286         
29287         
29288         this.doc.designMode="on";
29289         this.doc.open();
29290         this.doc.write(this.getDocMarkup());
29291         this.doc.close();
29292         
29293         var dbody = (this.doc.body || this.doc.documentElement);
29294         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
29295         // this copies styles from the containing element into thsi one..
29296         // not sure why we need all of this..
29297         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
29298         
29299         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
29300         //ss['background-attachment'] = 'fixed'; // w3c
29301         dbody.bgProperties = 'fixed'; // ie
29302         dbody.setAttribute("translate", "no");
29303         
29304         //Roo.DomHelper.applyStyles(dbody, ss);
29305         Roo.EventManager.on(this.doc, {
29306              
29307             'mouseup': this.onEditorEvent,
29308             'dblclick': this.onEditorEvent,
29309             'click': this.onEditorEvent,
29310             'keyup': this.onEditorEvent,
29311             
29312             buffer:100,
29313             scope: this
29314         });
29315         Roo.EventManager.on(this.doc, {
29316             'paste': this.onPasteEvent,
29317             scope : this
29318         });
29319         if(Roo.isGecko){
29320             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
29321         }
29322         //??? needed???
29323         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
29324             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
29325         }
29326         this.initialized = true;
29327
29328         
29329         // initialize special key events - enter
29330         new Roo.htmleditor.KeyEnter({core : this});
29331         
29332          
29333         
29334         this.owner.fireEvent('initialize', this);
29335         this.pushValue();
29336     },
29337     // this is to prevent a href clicks resulting in a redirect?
29338    
29339     onPasteEvent : function(e,v)
29340     {
29341         // I think we better assume paste is going to be a dirty load of rubish from word..
29342         
29343         // even pasting into a 'email version' of this widget will have to clean up that mess.
29344         var cd = (e.browserEvent.clipboardData || window.clipboardData);
29345         
29346         // check what type of paste - if it's an image, then handle it differently.
29347         if (cd.files && cd.files.length > 0) {
29348             // pasting images?
29349             var urlAPI = (window.createObjectURL && window) || 
29350                 (window.URL && URL.revokeObjectURL && URL) || 
29351                 (window.webkitURL && webkitURL);
29352     
29353             var url = urlAPI.createObjectURL( cd.files[0]);
29354             this.insertAtCursor('<img src=" + url + ">');
29355             return false;
29356         }
29357         if (cd.types.indexOf('text/html') < 0 ) {
29358             return false;
29359         }
29360         var images = [];
29361         var html = cd.getData('text/html'); // clipboard event
29362         if (cd.types.indexOf('text/rtf') > -1) {
29363             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
29364             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
29365         }
29366         //Roo.log(images);
29367         //Roo.log(imgs);
29368         // fixme..
29369         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
29370                        .map(function(g) { return g.toDataURL(); })
29371                        .filter(function(g) { return g != 'about:blank'; });
29372         
29373         
29374         html = this.cleanWordChars(html);
29375         
29376         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
29377         
29378         
29379         var sn = this.getParentElement();
29380         // check if d contains a table, and prevent nesting??
29381         //Roo.log(d.getElementsByTagName('table'));
29382         //Roo.log(sn);
29383         //Roo.log(sn.closest('table'));
29384         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
29385             e.preventDefault();
29386             this.insertAtCursor("You can not nest tables");
29387             //Roo.log("prevent?"); // fixme - 
29388             return false;
29389         }
29390         
29391         if (images.length > 0) {
29392             Roo.each(d.getElementsByTagName('img'), function(img, i) {
29393                 img.setAttribute('src', images[i]);
29394             });
29395         }
29396         if (this.autoClean) {
29397             new Roo.htmleditor.FilterWord({ node : d });
29398             
29399             new Roo.htmleditor.FilterStyleToTag({ node : d });
29400             new Roo.htmleditor.FilterAttributes({
29401                 node : d,
29402                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width'],
29403                 attrib_clean : ['href', 'src' ] 
29404             });
29405             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
29406             // should be fonts..
29407             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', 'O:P' ]} );
29408             new Roo.htmleditor.FilterParagraph({ node : d });
29409             new Roo.htmleditor.FilterSpan({ node : d });
29410             new Roo.htmleditor.FilterLongBr({ node : d });
29411             new Roo.htmleditor.FilterComment({ node : d });
29412             
29413             
29414         }
29415         if (this.enableBlocks) {
29416                 
29417             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
29418                 if (img.closest('figure')) { // assume!! that it's aready
29419                     return;
29420                 }
29421                 var fig  = new Roo.htmleditor.BlockFigure({
29422                     image_src  : img.src
29423                 });
29424                 fig.updateElement(img); // replace it..
29425                 
29426             });
29427         }
29428         
29429         
29430         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
29431         if (this.enableBlocks) {
29432             Roo.htmleditor.Block.initAll(this.doc.body);
29433         }
29434          
29435         
29436         e.preventDefault();
29437         return false;
29438         // default behaveiour should be our local cleanup paste? (optional?)
29439         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
29440         //this.owner.fireEvent('paste', e, v);
29441     },
29442     // private
29443     onDestroy : function(){
29444         
29445         
29446         
29447         if(this.rendered){
29448             
29449             //for (var i =0; i < this.toolbars.length;i++) {
29450             //    // fixme - ask toolbars for heights?
29451             //    this.toolbars[i].onDestroy();
29452            // }
29453             
29454             //this.wrap.dom.innerHTML = '';
29455             //this.wrap.remove();
29456         }
29457     },
29458
29459     // private
29460     onFirstFocus : function(){
29461         
29462         this.assignDocWin();
29463         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
29464         
29465         this.activated = true;
29466          
29467     
29468         if(Roo.isGecko){ // prevent silly gecko errors
29469             this.win.focus();
29470             var s = this.win.getSelection();
29471             if(!s.focusNode || s.focusNode.nodeType != 3){
29472                 var r = s.getRangeAt(0);
29473                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
29474                 r.collapse(true);
29475                 this.deferFocus();
29476             }
29477             try{
29478                 this.execCmd('useCSS', true);
29479                 this.execCmd('styleWithCSS', false);
29480             }catch(e){}
29481         }
29482         this.owner.fireEvent('activate', this);
29483     },
29484
29485     // private
29486     adjustFont: function(btn){
29487         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
29488         //if(Roo.isSafari){ // safari
29489         //    adjust *= 2;
29490        // }
29491         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
29492         if(Roo.isSafari){ // safari
29493             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
29494             v =  (v < 10) ? 10 : v;
29495             v =  (v > 48) ? 48 : v;
29496             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
29497             
29498         }
29499         
29500         
29501         v = Math.max(1, v+adjust);
29502         
29503         this.execCmd('FontSize', v  );
29504     },
29505
29506     onEditorEvent : function(e)
29507     {
29508          
29509         
29510         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
29511             return; // we do not handle this.. (undo manager does..)
29512         }
29513         // in theory this detects if the last element is not a br, then we try and do that.
29514         // its so clicking in space at bottom triggers adding a br and moving the cursor.
29515         if (e &&
29516             e.target.nodeName == 'BODY' &&
29517             e.type == "mouseup" &&
29518             this.doc.body.lastChild
29519            ) {
29520             var lc = this.doc.body.lastChild;
29521             // gtx-trans is google translate plugin adding crap.
29522             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
29523                 lc = lc.previousSibling;
29524             }
29525             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
29526             // if last element is <BR> - then dont do anything.
29527             
29528                 var ns = this.doc.createElement('br');
29529                 this.doc.body.appendChild(ns);
29530                 range = this.doc.createRange();
29531                 range.setStartAfter(ns);
29532                 range.collapse(true);
29533                 var sel = this.win.getSelection();
29534                 sel.removeAllRanges();
29535                 sel.addRange(range);
29536             }
29537         }
29538         
29539         
29540         
29541         this.fireEditorEvent(e);
29542       //  this.updateToolbar();
29543         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
29544     },
29545     
29546     fireEditorEvent: function(e)
29547     {
29548         this.owner.fireEvent('editorevent', this, e);
29549     },
29550
29551     insertTag : function(tg)
29552     {
29553         // could be a bit smarter... -> wrap the current selected tRoo..
29554         if (tg.toLowerCase() == 'span' ||
29555             tg.toLowerCase() == 'code' ||
29556             tg.toLowerCase() == 'sup' ||
29557             tg.toLowerCase() == 'sub' 
29558             ) {
29559             
29560             range = this.createRange(this.getSelection());
29561             var wrappingNode = this.doc.createElement(tg.toLowerCase());
29562             wrappingNode.appendChild(range.extractContents());
29563             range.insertNode(wrappingNode);
29564
29565             return;
29566             
29567             
29568             
29569         }
29570         this.execCmd("formatblock",   tg);
29571         this.undoManager.addEvent(); 
29572     },
29573     
29574     insertText : function(txt)
29575     {
29576         
29577         
29578         var range = this.createRange();
29579         range.deleteContents();
29580                //alert(Sender.getAttribute('label'));
29581                
29582         range.insertNode(this.doc.createTextNode(txt));
29583         this.undoManager.addEvent();
29584     } ,
29585     
29586      
29587
29588     /**
29589      * Executes a Midas editor command on the editor document and performs necessary focus and
29590      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
29591      * @param {String} cmd The Midas command
29592      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
29593      */
29594     relayCmd : function(cmd, value)
29595     {
29596         
29597         switch (cmd) {
29598             case 'justifyleft':
29599             case 'justifyright':
29600             case 'justifycenter':
29601                 // if we are in a cell, then we will adjust the
29602                 var n = this.getParentElement();
29603                 var td = n.closest('td');
29604                 if (td) {
29605                     var bl = Roo.htmleditor.Block.factory(td);
29606                     bl.textAlign = cmd.replace('justify','');
29607                     bl.updateElement();
29608                     this.owner.fireEvent('editorevent', this);
29609                     return;
29610                 }
29611                 this.execCmd('styleWithCSS', true); // 
29612                 break;
29613             case 'bold':
29614             case 'italic':
29615                 // if there is no selection, then we insert, and set the curson inside it..
29616                 this.execCmd('styleWithCSS', false); 
29617                 break;
29618                 
29619         
29620             default:
29621                 break;
29622         }
29623         
29624         
29625         this.win.focus();
29626         this.execCmd(cmd, value);
29627         this.owner.fireEvent('editorevent', this);
29628         //this.updateToolbar();
29629         this.owner.deferFocus();
29630     },
29631
29632     /**
29633      * Executes a Midas editor command directly on the editor document.
29634      * For visual commands, you should use {@link #relayCmd} instead.
29635      * <b>This should only be called after the editor is initialized.</b>
29636      * @param {String} cmd The Midas command
29637      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
29638      */
29639     execCmd : function(cmd, value){
29640         this.doc.execCommand(cmd, false, value === undefined ? null : value);
29641         this.syncValue();
29642     },
29643  
29644  
29645    
29646     /**
29647      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
29648      * to insert tRoo.
29649      * @param {String} text | dom node.. 
29650      */
29651     insertAtCursor : function(text)
29652     {
29653         
29654         if(!this.activated){
29655             return;
29656         }
29657          
29658         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
29659             this.win.focus();
29660             
29661             
29662             // from jquery ui (MIT licenced)
29663             var range, node;
29664             var win = this.win;
29665             
29666             if (win.getSelection && win.getSelection().getRangeAt) {
29667                 
29668                 // delete the existing?
29669                 
29670                 this.createRange(this.getSelection()).deleteContents();
29671                 range = win.getSelection().getRangeAt(0);
29672                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
29673                 range.insertNode(node);
29674                 range = range.cloneRange();
29675                 range.collapse(false);
29676                  
29677                 win.getSelection().removeAllRanges();
29678                 win.getSelection().addRange(range);
29679                 
29680                 
29681                 
29682             } else if (win.document.selection && win.document.selection.createRange) {
29683                 // no firefox support
29684                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
29685                 win.document.selection.createRange().pasteHTML(txt);
29686             
29687             } else {
29688                 // no firefox support
29689                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
29690                 this.execCmd('InsertHTML', txt);
29691             } 
29692             this.syncValue();
29693             
29694             this.deferFocus();
29695         }
29696     },
29697  // private
29698     mozKeyPress : function(e){
29699         if(e.ctrlKey){
29700             var c = e.getCharCode(), cmd;
29701           
29702             if(c > 0){
29703                 c = String.fromCharCode(c).toLowerCase();
29704                 switch(c){
29705                     case 'b':
29706                         cmd = 'bold';
29707                         break;
29708                     case 'i':
29709                         cmd = 'italic';
29710                         break;
29711                     
29712                     case 'u':
29713                         cmd = 'underline';
29714                         break;
29715                     
29716                     //case 'v':
29717                       //  this.cleanUpPaste.defer(100, this);
29718                       //  return;
29719                         
29720                 }
29721                 if(cmd){
29722                     
29723                     this.relayCmd(cmd);
29724                     //this.win.focus();
29725                     //this.execCmd(cmd);
29726                     //this.deferFocus();
29727                     e.preventDefault();
29728                 }
29729                 
29730             }
29731         }
29732     },
29733
29734     // private
29735     fixKeys : function(){ // load time branching for fastest keydown performance
29736         
29737         
29738         if(Roo.isIE){
29739             return function(e){
29740                 var k = e.getKey(), r;
29741                 if(k == e.TAB){
29742                     e.stopEvent();
29743                     r = this.doc.selection.createRange();
29744                     if(r){
29745                         r.collapse(true);
29746                         r.pasteHTML('&#160;&#160;&#160;&#160;');
29747                         this.deferFocus();
29748                     }
29749                     return;
29750                 }
29751                 /// this is handled by Roo.htmleditor.KeyEnter
29752                  /*
29753                 if(k == e.ENTER){
29754                     r = this.doc.selection.createRange();
29755                     if(r){
29756                         var target = r.parentElement();
29757                         if(!target || target.tagName.toLowerCase() != 'li'){
29758                             e.stopEvent();
29759                             r.pasteHTML('<br/>');
29760                             r.collapse(false);
29761                             r.select();
29762                         }
29763                     }
29764                 }
29765                 */
29766                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29767                 //    this.cleanUpPaste.defer(100, this);
29768                 //    return;
29769                 //}
29770                 
29771                 
29772             };
29773         }else if(Roo.isOpera){
29774             return function(e){
29775                 var k = e.getKey();
29776                 if(k == e.TAB){
29777                     e.stopEvent();
29778                     this.win.focus();
29779                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
29780                     this.deferFocus();
29781                 }
29782                
29783                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29784                 //    this.cleanUpPaste.defer(100, this);
29785                  //   return;
29786                 //}
29787                 
29788             };
29789         }else if(Roo.isSafari){
29790             return function(e){
29791                 var k = e.getKey();
29792                 
29793                 if(k == e.TAB){
29794                     e.stopEvent();
29795                     this.execCmd('InsertText','\t');
29796                     this.deferFocus();
29797                     return;
29798                 }
29799                  this.mozKeyPress(e);
29800                 
29801                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29802                  //   this.cleanUpPaste.defer(100, this);
29803                  //   return;
29804                // }
29805                 
29806              };
29807         }
29808     }(),
29809     
29810     getAllAncestors: function()
29811     {
29812         var p = this.getSelectedNode();
29813         var a = [];
29814         if (!p) {
29815             a.push(p); // push blank onto stack..
29816             p = this.getParentElement();
29817         }
29818         
29819         
29820         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
29821             a.push(p);
29822             p = p.parentNode;
29823         }
29824         a.push(this.doc.body);
29825         return a;
29826     },
29827     lastSel : false,
29828     lastSelNode : false,
29829     
29830     
29831     getSelection : function() 
29832     {
29833         this.assignDocWin();
29834         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
29835     },
29836     /**
29837      * Select a dom node
29838      * @param {DomElement} node the node to select
29839      */
29840     selectNode : function(node, collapse)
29841     {
29842         var nodeRange = node.ownerDocument.createRange();
29843         try {
29844             nodeRange.selectNode(node);
29845         } catch (e) {
29846             nodeRange.selectNodeContents(node);
29847         }
29848         if (collapse === true) {
29849             nodeRange.collapse(true);
29850         }
29851         //
29852         var s = this.win.getSelection();
29853         s.removeAllRanges();
29854         s.addRange(nodeRange);
29855     },
29856     
29857     getSelectedNode: function() 
29858     {
29859         // this may only work on Gecko!!!
29860         
29861         // should we cache this!!!!
29862         
29863          
29864          
29865         var range = this.createRange(this.getSelection()).cloneRange();
29866         
29867         if (Roo.isIE) {
29868             var parent = range.parentElement();
29869             while (true) {
29870                 var testRange = range.duplicate();
29871                 testRange.moveToElementText(parent);
29872                 if (testRange.inRange(range)) {
29873                     break;
29874                 }
29875                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
29876                     break;
29877                 }
29878                 parent = parent.parentElement;
29879             }
29880             return parent;
29881         }
29882         
29883         // is ancestor a text element.
29884         var ac =  range.commonAncestorContainer;
29885         if (ac.nodeType == 3) {
29886             ac = ac.parentNode;
29887         }
29888         
29889         var ar = ac.childNodes;
29890          
29891         var nodes = [];
29892         var other_nodes = [];
29893         var has_other_nodes = false;
29894         for (var i=0;i<ar.length;i++) {
29895             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
29896                 continue;
29897             }
29898             // fullly contained node.
29899             
29900             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
29901                 nodes.push(ar[i]);
29902                 continue;
29903             }
29904             
29905             // probably selected..
29906             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
29907                 other_nodes.push(ar[i]);
29908                 continue;
29909             }
29910             // outer..
29911             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
29912                 continue;
29913             }
29914             
29915             
29916             has_other_nodes = true;
29917         }
29918         if (!nodes.length && other_nodes.length) {
29919             nodes= other_nodes;
29920         }
29921         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
29922             return false;
29923         }
29924         
29925         return nodes[0];
29926     },
29927     
29928     
29929     createRange: function(sel)
29930     {
29931         // this has strange effects when using with 
29932         // top toolbar - not sure if it's a great idea.
29933         //this.editor.contentWindow.focus();
29934         if (typeof sel != "undefined") {
29935             try {
29936                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
29937             } catch(e) {
29938                 return this.doc.createRange();
29939             }
29940         } else {
29941             return this.doc.createRange();
29942         }
29943     },
29944     getParentElement: function()
29945     {
29946         
29947         this.assignDocWin();
29948         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
29949         
29950         var range = this.createRange(sel);
29951          
29952         try {
29953             var p = range.commonAncestorContainer;
29954             while (p.nodeType == 3) { // text node
29955                 p = p.parentNode;
29956             }
29957             return p;
29958         } catch (e) {
29959             return null;
29960         }
29961     
29962     },
29963     /***
29964      *
29965      * Range intersection.. the hard stuff...
29966      *  '-1' = before
29967      *  '0' = hits..
29968      *  '1' = after.
29969      *         [ -- selected range --- ]
29970      *   [fail]                        [fail]
29971      *
29972      *    basically..
29973      *      if end is before start or  hits it. fail.
29974      *      if start is after end or hits it fail.
29975      *
29976      *   if either hits (but other is outside. - then it's not 
29977      *   
29978      *    
29979      **/
29980     
29981     
29982     // @see http://www.thismuchiknow.co.uk/?p=64.
29983     rangeIntersectsNode : function(range, node)
29984     {
29985         var nodeRange = node.ownerDocument.createRange();
29986         try {
29987             nodeRange.selectNode(node);
29988         } catch (e) {
29989             nodeRange.selectNodeContents(node);
29990         }
29991     
29992         var rangeStartRange = range.cloneRange();
29993         rangeStartRange.collapse(true);
29994     
29995         var rangeEndRange = range.cloneRange();
29996         rangeEndRange.collapse(false);
29997     
29998         var nodeStartRange = nodeRange.cloneRange();
29999         nodeStartRange.collapse(true);
30000     
30001         var nodeEndRange = nodeRange.cloneRange();
30002         nodeEndRange.collapse(false);
30003     
30004         return rangeStartRange.compareBoundaryPoints(
30005                  Range.START_TO_START, nodeEndRange) == -1 &&
30006                rangeEndRange.compareBoundaryPoints(
30007                  Range.START_TO_START, nodeStartRange) == 1;
30008         
30009          
30010     },
30011     rangeCompareNode : function(range, node)
30012     {
30013         var nodeRange = node.ownerDocument.createRange();
30014         try {
30015             nodeRange.selectNode(node);
30016         } catch (e) {
30017             nodeRange.selectNodeContents(node);
30018         }
30019         
30020         
30021         range.collapse(true);
30022     
30023         nodeRange.collapse(true);
30024      
30025         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
30026         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
30027          
30028         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
30029         
30030         var nodeIsBefore   =  ss == 1;
30031         var nodeIsAfter    = ee == -1;
30032         
30033         if (nodeIsBefore && nodeIsAfter) {
30034             return 0; // outer
30035         }
30036         if (!nodeIsBefore && nodeIsAfter) {
30037             return 1; //right trailed.
30038         }
30039         
30040         if (nodeIsBefore && !nodeIsAfter) {
30041             return 2;  // left trailed.
30042         }
30043         // fully contined.
30044         return 3;
30045     },
30046  
30047     cleanWordChars : function(input) {// change the chars to hex code
30048         
30049        var swapCodes  = [ 
30050             [    8211, "&#8211;" ], 
30051             [    8212, "&#8212;" ], 
30052             [    8216,  "'" ],  
30053             [    8217, "'" ],  
30054             [    8220, '"' ],  
30055             [    8221, '"' ],  
30056             [    8226, "*" ],  
30057             [    8230, "..." ]
30058         ]; 
30059         var output = input;
30060         Roo.each(swapCodes, function(sw) { 
30061             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
30062             
30063             output = output.replace(swapper, sw[1]);
30064         });
30065         
30066         return output;
30067     },
30068     
30069      
30070     
30071         
30072     
30073     cleanUpChild : function (node)
30074     {
30075         
30076         new Roo.htmleditor.FilterComment({node : node});
30077         new Roo.htmleditor.FilterAttributes({
30078                 node : node,
30079                 attrib_black : this.ablack,
30080                 attrib_clean : this.aclean,
30081                 style_white : this.cwhite,
30082                 style_black : this.cblack
30083         });
30084         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
30085         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
30086          
30087         
30088     },
30089     
30090     /**
30091      * Clean up MS wordisms...
30092      * @deprecated - use filter directly
30093      */
30094     cleanWord : function(node)
30095     {
30096         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
30097         
30098     },
30099    
30100     
30101     /**
30102
30103      * @deprecated - use filters
30104      */
30105     cleanTableWidths : function(node)
30106     {
30107         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
30108         
30109  
30110     },
30111     
30112      
30113         
30114     applyBlacklists : function()
30115     {
30116         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
30117         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
30118         
30119         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
30120         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
30121         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
30122         
30123         this.white = [];
30124         this.black = [];
30125         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
30126             if (b.indexOf(tag) > -1) {
30127                 return;
30128             }
30129             this.white.push(tag);
30130             
30131         }, this);
30132         
30133         Roo.each(w, function(tag) {
30134             if (b.indexOf(tag) > -1) {
30135                 return;
30136             }
30137             if (this.white.indexOf(tag) > -1) {
30138                 return;
30139             }
30140             this.white.push(tag);
30141             
30142         }, this);
30143         
30144         
30145         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
30146             if (w.indexOf(tag) > -1) {
30147                 return;
30148             }
30149             this.black.push(tag);
30150             
30151         }, this);
30152         
30153         Roo.each(b, function(tag) {
30154             if (w.indexOf(tag) > -1) {
30155                 return;
30156             }
30157             if (this.black.indexOf(tag) > -1) {
30158                 return;
30159             }
30160             this.black.push(tag);
30161             
30162         }, this);
30163         
30164         
30165         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
30166         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
30167         
30168         this.cwhite = [];
30169         this.cblack = [];
30170         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
30171             if (b.indexOf(tag) > -1) {
30172                 return;
30173             }
30174             this.cwhite.push(tag);
30175             
30176         }, this);
30177         
30178         Roo.each(w, function(tag) {
30179             if (b.indexOf(tag) > -1) {
30180                 return;
30181             }
30182             if (this.cwhite.indexOf(tag) > -1) {
30183                 return;
30184             }
30185             this.cwhite.push(tag);
30186             
30187         }, this);
30188         
30189         
30190         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
30191             if (w.indexOf(tag) > -1) {
30192                 return;
30193             }
30194             this.cblack.push(tag);
30195             
30196         }, this);
30197         
30198         Roo.each(b, function(tag) {
30199             if (w.indexOf(tag) > -1) {
30200                 return;
30201             }
30202             if (this.cblack.indexOf(tag) > -1) {
30203                 return;
30204             }
30205             this.cblack.push(tag);
30206             
30207         }, this);
30208     },
30209     
30210     setStylesheets : function(stylesheets)
30211     {
30212         if(typeof(stylesheets) == 'string'){
30213             Roo.get(this.iframe.contentDocument.head).createChild({
30214                 tag : 'link',
30215                 rel : 'stylesheet',
30216                 type : 'text/css',
30217                 href : stylesheets
30218             });
30219             
30220             return;
30221         }
30222         var _this = this;
30223      
30224         Roo.each(stylesheets, function(s) {
30225             if(!s.length){
30226                 return;
30227             }
30228             
30229             Roo.get(_this.iframe.contentDocument.head).createChild({
30230                 tag : 'link',
30231                 rel : 'stylesheet',
30232                 type : 'text/css',
30233                 href : s
30234             });
30235         });
30236
30237         
30238     },
30239     
30240     
30241     updateLanguage : function()
30242     {
30243         if (!this.iframe || !this.iframe.contentDocument) {
30244             return;
30245         }
30246         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
30247     },
30248     
30249     
30250     removeStylesheets : function()
30251     {
30252         var _this = this;
30253         
30254         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
30255             s.remove();
30256         });
30257     },
30258     
30259     setStyle : function(style)
30260     {
30261         Roo.get(this.iframe.contentDocument.head).createChild({
30262             tag : 'style',
30263             type : 'text/css',
30264             html : style
30265         });
30266
30267         return;
30268     }
30269     
30270     // hide stuff that is not compatible
30271     /**
30272      * @event blur
30273      * @hide
30274      */
30275     /**
30276      * @event change
30277      * @hide
30278      */
30279     /**
30280      * @event focus
30281      * @hide
30282      */
30283     /**
30284      * @event specialkey
30285      * @hide
30286      */
30287     /**
30288      * @cfg {String} fieldClass @hide
30289      */
30290     /**
30291      * @cfg {String} focusClass @hide
30292      */
30293     /**
30294      * @cfg {String} autoCreate @hide
30295      */
30296     /**
30297      * @cfg {String} inputType @hide
30298      */
30299     /**
30300      * @cfg {String} invalidClass @hide
30301      */
30302     /**
30303      * @cfg {String} invalidText @hide
30304      */
30305     /**
30306      * @cfg {String} msgFx @hide
30307      */
30308     /**
30309      * @cfg {String} validateOnBlur @hide
30310      */
30311 });
30312
30313 Roo.HtmlEditorCore.white = [
30314         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
30315         
30316        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
30317        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
30318        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
30319        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
30320        'TABLE',   'UL',         'XMP', 
30321        
30322        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
30323       'THEAD',   'TR', 
30324      
30325       'DIR', 'MENU', 'OL', 'UL', 'DL',
30326        
30327       'EMBED',  'OBJECT'
30328 ];
30329
30330
30331 Roo.HtmlEditorCore.black = [
30332     //    'embed',  'object', // enable - backend responsiblity to clean thiese
30333         'APPLET', // 
30334         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
30335         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
30336         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
30337         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
30338         //'FONT' // CLEAN LATER..
30339         'COLGROUP', 'COL'   // messy tables.
30340         
30341         
30342 ];
30343 Roo.HtmlEditorCore.clean = [ // ?? needed???
30344      'SCRIPT', 'STYLE', 'TITLE', 'XML'
30345 ];
30346 Roo.HtmlEditorCore.tag_remove = [
30347     'FONT', 'TBODY'  
30348 ];
30349 // attributes..
30350
30351 Roo.HtmlEditorCore.ablack = [
30352     'on'
30353 ];
30354     
30355 Roo.HtmlEditorCore.aclean = [ 
30356     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
30357 ];
30358
30359 // protocols..
30360 Roo.HtmlEditorCore.pwhite= [
30361         'http',  'https',  'mailto'
30362 ];
30363
30364 // white listed style attributes.
30365 Roo.HtmlEditorCore.cwhite= [
30366       //  'text-align', /// default is to allow most things..
30367       
30368          
30369 //        'font-size'//??
30370 ];
30371
30372 // black listed style attributes.
30373 Roo.HtmlEditorCore.cblack= [
30374       //  'font-size' -- this can be set by the project 
30375 ];
30376
30377
30378
30379
30380     /*
30381  * - LGPL
30382  *
30383  * HtmlEditor
30384  * 
30385  */
30386
30387 /**
30388  * @class Roo.bootstrap.form.HtmlEditor
30389  * @extends Roo.bootstrap.form.TextArea
30390  * Bootstrap HtmlEditor class
30391
30392  * @constructor
30393  * Create a new HtmlEditor
30394  * @param {Object} config The config object
30395  */
30396
30397 Roo.bootstrap.form.HtmlEditor = function(config){
30398     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
30399     if (!this.toolbars) {
30400         this.toolbars = [];
30401     }
30402     
30403     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
30404     this.addEvents({
30405             /**
30406              * @event initialize
30407              * Fires when the editor is fully initialized (including the iframe)
30408              * @param {HtmlEditor} this
30409              */
30410             initialize: true,
30411             /**
30412              * @event activate
30413              * Fires when the editor is first receives the focus. Any insertion must wait
30414              * until after this event.
30415              * @param {HtmlEditor} this
30416              */
30417             activate: true,
30418              /**
30419              * @event beforesync
30420              * Fires before the textarea is updated with content from the editor iframe. Return false
30421              * to cancel the sync.
30422              * @param {HtmlEditor} this
30423              * @param {String} html
30424              */
30425             beforesync: true,
30426              /**
30427              * @event beforepush
30428              * Fires before the iframe editor is updated with content from the textarea. Return false
30429              * to cancel the push.
30430              * @param {HtmlEditor} this
30431              * @param {String} html
30432              */
30433             beforepush: true,
30434              /**
30435              * @event sync
30436              * Fires when the textarea is updated with content from the editor iframe.
30437              * @param {HtmlEditor} this
30438              * @param {String} html
30439              */
30440             sync: true,
30441              /**
30442              * @event push
30443              * Fires when the iframe editor is updated with content from the textarea.
30444              * @param {HtmlEditor} this
30445              * @param {String} html
30446              */
30447             push: true,
30448              /**
30449              * @event editmodechange
30450              * Fires when the editor switches edit modes
30451              * @param {HtmlEditor} this
30452              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
30453              */
30454             editmodechange: true,
30455             /**
30456              * @event editorevent
30457              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30458              * @param {HtmlEditor} this
30459              */
30460             editorevent: true,
30461             /**
30462              * @event firstfocus
30463              * Fires when on first focus - needed by toolbars..
30464              * @param {HtmlEditor} this
30465              */
30466             firstfocus: true,
30467             /**
30468              * @event autosave
30469              * Auto save the htmlEditor value as a file into Events
30470              * @param {HtmlEditor} this
30471              */
30472             autosave: true,
30473             /**
30474              * @event savedpreview
30475              * preview the saved version of htmlEditor
30476              * @param {HtmlEditor} this
30477              */
30478             savedpreview: true
30479         });
30480 };
30481
30482
30483 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
30484     
30485     
30486       /**
30487      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
30488      */
30489     toolbars : false,
30490     
30491      /**
30492     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
30493     */
30494     btns : [],
30495    
30496      /**
30497      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
30498      *                        Roo.resizable.
30499      */
30500     resizable : false,
30501      /**
30502      * @cfg {Number} height (in pixels)
30503      */   
30504     height: 300,
30505    /**
30506      * @cfg {Number} width (in pixels)
30507      */   
30508     width: false,
30509     
30510     /**
30511      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30512      * 
30513      */
30514     stylesheets: false,
30515     
30516     // id of frame..
30517     frameId: false,
30518     
30519     // private properties
30520     validationEvent : false,
30521     deferHeight: true,
30522     initialized : false,
30523     activated : false,
30524     
30525     onFocus : Roo.emptyFn,
30526     iframePad:3,
30527     hideMode:'offsets',
30528     
30529     tbContainer : false,
30530     
30531     bodyCls : '',
30532     
30533     toolbarContainer :function() {
30534         return this.wrap.select('.x-html-editor-tb',true).first();
30535     },
30536
30537     /**
30538      * Protected method that will not generally be called directly. It
30539      * is called when the editor creates its toolbar. Override this method if you need to
30540      * add custom toolbar buttons.
30541      * @param {HtmlEditor} editor
30542      */
30543     createToolbar : function(){
30544         Roo.log('renewing');
30545         Roo.log("create toolbars");
30546         
30547         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
30548         this.toolbars[0].render(this.toolbarContainer());
30549         
30550         return;
30551         
30552 //        if (!editor.toolbars || !editor.toolbars.length) {
30553 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
30554 //        }
30555 //        
30556 //        for (var i =0 ; i < editor.toolbars.length;i++) {
30557 //            editor.toolbars[i] = Roo.factory(
30558 //                    typeof(editor.toolbars[i]) == 'string' ?
30559 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
30560 //                Roo.bootstrap.form.HtmlEditor);
30561 //            editor.toolbars[i].init(editor);
30562 //        }
30563     },
30564
30565      
30566     // private
30567     onRender : function(ct, position)
30568     {
30569        // Roo.log("Call onRender: " + this.xtype);
30570         var _t = this;
30571         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
30572       
30573         this.wrap = this.inputEl().wrap({
30574             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
30575         });
30576         
30577         this.editorcore.onRender(ct, position);
30578          
30579         if (this.resizable) {
30580             this.resizeEl = new Roo.Resizable(this.wrap, {
30581                 pinned : true,
30582                 wrap: true,
30583                 dynamic : true,
30584                 minHeight : this.height,
30585                 height: this.height,
30586                 handles : this.resizable,
30587                 width: this.width,
30588                 listeners : {
30589                     resize : function(r, w, h) {
30590                         _t.onResize(w,h); // -something
30591                     }
30592                 }
30593             });
30594             
30595         }
30596         this.createToolbar(this);
30597        
30598         
30599         if(!this.width && this.resizable){
30600             this.setSize(this.wrap.getSize());
30601         }
30602         if (this.resizeEl) {
30603             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
30604             // should trigger onReize..
30605         }
30606         
30607     },
30608
30609     // private
30610     onResize : function(w, h)
30611     {
30612         Roo.log('resize: ' +w + ',' + h );
30613         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
30614         var ew = false;
30615         var eh = false;
30616         
30617         if(this.inputEl() ){
30618             if(typeof w == 'number'){
30619                 var aw = w - this.wrap.getFrameWidth('lr');
30620                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
30621                 ew = aw;
30622             }
30623             if(typeof h == 'number'){
30624                  var tbh = -11;  // fixme it needs to tool bar size!
30625                 for (var i =0; i < this.toolbars.length;i++) {
30626                     // fixme - ask toolbars for heights?
30627                     tbh += this.toolbars[i].el.getHeight();
30628                     //if (this.toolbars[i].footer) {
30629                     //    tbh += this.toolbars[i].footer.el.getHeight();
30630                     //}
30631                 }
30632               
30633                 
30634                 
30635                 
30636                 
30637                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
30638                 ah -= 5; // knock a few pixes off for look..
30639                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
30640                 var eh = ah;
30641             }
30642         }
30643         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
30644         this.editorcore.onResize(ew,eh);
30645         
30646     },
30647
30648     /**
30649      * Toggles the editor between standard and source edit mode.
30650      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
30651      */
30652     toggleSourceEdit : function(sourceEditMode)
30653     {
30654         this.editorcore.toggleSourceEdit(sourceEditMode);
30655         
30656         if(this.editorcore.sourceEditMode){
30657             Roo.log('editor - showing textarea');
30658             
30659 //            Roo.log('in');
30660 //            Roo.log(this.syncValue());
30661             this.syncValue();
30662             this.inputEl().removeClass(['hide', 'x-hidden']);
30663             this.inputEl().dom.removeAttribute('tabIndex');
30664             this.inputEl().focus();
30665         }else{
30666             Roo.log('editor - hiding textarea');
30667 //            Roo.log('out')
30668 //            Roo.log(this.pushValue()); 
30669             this.pushValue();
30670             
30671             this.inputEl().addClass(['hide', 'x-hidden']);
30672             this.inputEl().dom.setAttribute('tabIndex', -1);
30673             //this.deferFocus();
30674         }
30675          
30676         if(this.resizable){
30677             this.setSize(this.wrap.getSize());
30678         }
30679         
30680         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
30681     },
30682  
30683     // private (for BoxComponent)
30684     adjustSize : Roo.BoxComponent.prototype.adjustSize,
30685
30686     // private (for BoxComponent)
30687     getResizeEl : function(){
30688         return this.wrap;
30689     },
30690
30691     // private (for BoxComponent)
30692     getPositionEl : function(){
30693         return this.wrap;
30694     },
30695
30696     // private
30697     initEvents : function(){
30698         this.originalValue = this.getValue();
30699     },
30700
30701 //    /**
30702 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
30703 //     * @method
30704 //     */
30705 //    markInvalid : Roo.emptyFn,
30706 //    /**
30707 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
30708 //     * @method
30709 //     */
30710 //    clearInvalid : Roo.emptyFn,
30711
30712     setValue : function(v){
30713         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
30714         this.editorcore.pushValue();
30715     },
30716
30717      
30718     // private
30719     deferFocus : function(){
30720         this.focus.defer(10, this);
30721     },
30722
30723     // doc'ed in Field
30724     focus : function(){
30725         this.editorcore.focus();
30726         
30727     },
30728       
30729
30730     // private
30731     onDestroy : function(){
30732         
30733         
30734         
30735         if(this.rendered){
30736             
30737             for (var i =0; i < this.toolbars.length;i++) {
30738                 // fixme - ask toolbars for heights?
30739                 this.toolbars[i].onDestroy();
30740             }
30741             
30742             this.wrap.dom.innerHTML = '';
30743             this.wrap.remove();
30744         }
30745     },
30746
30747     // private
30748     onFirstFocus : function(){
30749         //Roo.log("onFirstFocus");
30750         this.editorcore.onFirstFocus();
30751          for (var i =0; i < this.toolbars.length;i++) {
30752             this.toolbars[i].onFirstFocus();
30753         }
30754         
30755     },
30756     
30757     // private
30758     syncValue : function()
30759     {   
30760         this.editorcore.syncValue();
30761     },
30762     
30763     pushValue : function()
30764     {   
30765         this.editorcore.pushValue();
30766     }
30767      
30768     
30769     // hide stuff that is not compatible
30770     /**
30771      * @event blur
30772      * @hide
30773      */
30774     /**
30775      * @event change
30776      * @hide
30777      */
30778     /**
30779      * @event focus
30780      * @hide
30781      */
30782     /**
30783      * @event specialkey
30784      * @hide
30785      */
30786     /**
30787      * @cfg {String} fieldClass @hide
30788      */
30789     /**
30790      * @cfg {String} focusClass @hide
30791      */
30792     /**
30793      * @cfg {String} autoCreate @hide
30794      */
30795     /**
30796      * @cfg {String} inputType @hide
30797      */
30798      
30799     /**
30800      * @cfg {String} invalidText @hide
30801      */
30802     /**
30803      * @cfg {String} msgFx @hide
30804      */
30805     /**
30806      * @cfg {String} validateOnBlur @hide
30807      */
30808 });
30809  
30810     
30811    
30812    
30813    
30814       
30815 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
30816 /**
30817  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
30818  * @parent Roo.bootstrap.form.HtmlEditor
30819  * @extends Roo.bootstrap.nav.Simplebar
30820  * Basic Toolbar
30821  * 
30822  * @example
30823  * Usage:
30824  *
30825  new Roo.bootstrap.form.HtmlEditor({
30826     ....
30827     toolbars : [
30828         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
30829             disable : { fonts: 1 , format: 1, ..., ... , ...],
30830             btns : [ .... ]
30831         })
30832     }
30833      
30834  * 
30835  * @cfg {Object} disable List of elements to disable..
30836  * @cfg {Array} btns List of additional buttons.
30837  * 
30838  * 
30839  * NEEDS Extra CSS? 
30840  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
30841  */
30842  
30843 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
30844 {
30845     
30846     Roo.apply(this, config);
30847     
30848     // default disabled, based on 'good practice'..
30849     this.disable = this.disable || {};
30850     Roo.applyIf(this.disable, {
30851         fontSize : true,
30852         colors : true,
30853         specialElements : true
30854     });
30855     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
30856     
30857     this.editor = config.editor;
30858     this.editorcore = config.editor.editorcore;
30859     
30860     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
30861     
30862     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
30863     // dont call parent... till later.
30864 }
30865 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
30866      
30867     bar : true,
30868     
30869     editor : false,
30870     editorcore : false,
30871     
30872     
30873     formats : [
30874         "p" ,  
30875         "h1","h2","h3","h4","h5","h6", 
30876         "pre", "code", 
30877         "abbr", "acronym", "address", "cite", "samp", "var",
30878         'div','span'
30879     ],
30880     
30881     onRender : function(ct, position)
30882     {
30883        // Roo.log("Call onRender: " + this.xtype);
30884         
30885        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
30886        Roo.log(this.el);
30887        this.el.dom.style.marginBottom = '0';
30888        var _this = this;
30889        var editorcore = this.editorcore;
30890        var editor= this.editor;
30891        
30892        var children = [];
30893        var btn = function(id,cmd , toggle, handler, html){
30894        
30895             var  event = toggle ? 'toggle' : 'click';
30896        
30897             var a = {
30898                 size : 'sm',
30899                 xtype: 'Button',
30900                 xns: Roo.bootstrap,
30901                 //glyphicon : id,
30902                 fa: id,
30903                 cmd : id || cmd,
30904                 enableToggle:toggle !== false,
30905                 html : html || '',
30906                 pressed : toggle ? false : null,
30907                 listeners : {}
30908             };
30909             a.listeners[toggle ? 'toggle' : 'click'] = function() {
30910                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
30911             };
30912             children.push(a);
30913             return a;
30914        }
30915        
30916     //    var cb_box = function...
30917         
30918         var style = {
30919                 xtype: 'Button',
30920                 size : 'sm',
30921                 xns: Roo.bootstrap,
30922                 fa : 'font',
30923                 //html : 'submit'
30924                 menu : {
30925                     xtype: 'Menu',
30926                     xns: Roo.bootstrap,
30927                     items:  []
30928                 }
30929         };
30930         Roo.each(this.formats, function(f) {
30931             style.menu.items.push({
30932                 xtype :'MenuItem',
30933                 xns: Roo.bootstrap,
30934                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
30935                 tagname : f,
30936                 listeners : {
30937                     click : function()
30938                     {
30939                         editorcore.insertTag(this.tagname);
30940                         editor.focus();
30941                     }
30942                 }
30943                 
30944             });
30945         });
30946         children.push(style);   
30947         
30948         btn('bold',false,true);
30949         btn('italic',false,true);
30950         btn('align-left', 'justifyleft',true);
30951         btn('align-center', 'justifycenter',true);
30952         btn('align-right' , 'justifyright',true);
30953         btn('link', false, false, function(btn) {
30954             //Roo.log("create link?");
30955             var url = prompt(this.createLinkText, this.defaultLinkValue);
30956             if(url && url != 'http:/'+'/'){
30957                 this.editorcore.relayCmd('createlink', url);
30958             }
30959         }),
30960         btn('list','insertunorderedlist',true);
30961         btn('pencil', false,true, function(btn){
30962                 Roo.log(this);
30963                 this.toggleSourceEdit(btn.pressed);
30964         });
30965         
30966         if (this.editor.btns.length > 0) {
30967             for (var i = 0; i<this.editor.btns.length; i++) {
30968                 children.push(this.editor.btns[i]);
30969             }
30970         }
30971         
30972         /*
30973         var cog = {
30974                 xtype: 'Button',
30975                 size : 'sm',
30976                 xns: Roo.bootstrap,
30977                 glyphicon : 'cog',
30978                 //html : 'submit'
30979                 menu : {
30980                     xtype: 'Menu',
30981                     xns: Roo.bootstrap,
30982                     items:  []
30983                 }
30984         };
30985         
30986         cog.menu.items.push({
30987             xtype :'MenuItem',
30988             xns: Roo.bootstrap,
30989             html : Clean styles,
30990             tagname : f,
30991             listeners : {
30992                 click : function()
30993                 {
30994                     editorcore.insertTag(this.tagname);
30995                     editor.focus();
30996                 }
30997             }
30998             
30999         });
31000        */
31001         
31002          
31003        this.xtype = 'NavSimplebar';
31004         
31005         for(var i=0;i< children.length;i++) {
31006             
31007             this.buttons.add(this.addxtypeChild(children[i]));
31008             
31009         }
31010         
31011         editor.on('editorevent', this.updateToolbar, this);
31012     },
31013     onBtnClick : function(id)
31014     {
31015        this.editorcore.relayCmd(id);
31016        this.editorcore.focus();
31017     },
31018     
31019     /**
31020      * Protected method that will not generally be called directly. It triggers
31021      * a toolbar update by reading the markup state of the current selection in the editor.
31022      */
31023     updateToolbar: function(){
31024
31025         if(!this.editorcore.activated){
31026             this.editor.onFirstFocus(); // is this neeed?
31027             return;
31028         }
31029
31030         var btns = this.buttons; 
31031         var doc = this.editorcore.doc;
31032         btns.get('bold').setActive(doc.queryCommandState('bold'));
31033         btns.get('italic').setActive(doc.queryCommandState('italic'));
31034         //btns.get('underline').setActive(doc.queryCommandState('underline'));
31035         
31036         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
31037         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
31038         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
31039         
31040         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
31041         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
31042          /*
31043         
31044         var ans = this.editorcore.getAllAncestors();
31045         if (this.formatCombo) {
31046             
31047             
31048             var store = this.formatCombo.store;
31049             this.formatCombo.setValue("");
31050             for (var i =0; i < ans.length;i++) {
31051                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
31052                     // select it..
31053                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
31054                     break;
31055                 }
31056             }
31057         }
31058         
31059         
31060         
31061         // hides menus... - so this cant be on a menu...
31062         Roo.bootstrap.MenuMgr.hideAll();
31063         */
31064         Roo.bootstrap.menu.Manager.hideAll();
31065         //this.editorsyncValue();
31066     },
31067     onFirstFocus: function() {
31068         this.buttons.each(function(item){
31069            item.enable();
31070         });
31071     },
31072     toggleSourceEdit : function(sourceEditMode){
31073         
31074           
31075         if(sourceEditMode){
31076             Roo.log("disabling buttons");
31077            this.buttons.each( function(item){
31078                 if(item.cmd != 'pencil'){
31079                     item.disable();
31080                 }
31081             });
31082           
31083         }else{
31084             Roo.log("enabling buttons");
31085             if(this.editorcore.initialized){
31086                 this.buttons.each( function(item){
31087                     item.enable();
31088                 });
31089             }
31090             
31091         }
31092         Roo.log("calling toggole on editor");
31093         // tell the editor that it's been pressed..
31094         this.editor.toggleSourceEdit(sourceEditMode);
31095        
31096     }
31097 });
31098
31099
31100
31101
31102  
31103 /*
31104  * - LGPL
31105  */
31106
31107 /**
31108  * @class Roo.bootstrap.form.Markdown
31109  * @extends Roo.bootstrap.form.TextArea
31110  * Bootstrap Showdown editable area
31111  * @cfg {string} content
31112  * 
31113  * @constructor
31114  * Create a new Showdown
31115  */
31116
31117 Roo.bootstrap.form.Markdown = function(config){
31118     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
31119    
31120 };
31121
31122 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
31123     
31124     editing :false,
31125     
31126     initEvents : function()
31127     {
31128         
31129         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
31130         this.markdownEl = this.el.createChild({
31131             cls : 'roo-markdown-area'
31132         });
31133         this.inputEl().addClass('d-none');
31134         if (this.getValue() == '') {
31135             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
31136             
31137         } else {
31138             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
31139         }
31140         this.markdownEl.on('click', this.toggleTextEdit, this);
31141         this.on('blur', this.toggleTextEdit, this);
31142         this.on('specialkey', this.resizeTextArea, this);
31143     },
31144     
31145     toggleTextEdit : function()
31146     {
31147         var sh = this.markdownEl.getHeight();
31148         this.inputEl().addClass('d-none');
31149         this.markdownEl.addClass('d-none');
31150         if (!this.editing) {
31151             // show editor?
31152             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
31153             this.inputEl().removeClass('d-none');
31154             this.inputEl().focus();
31155             this.editing = true;
31156             return;
31157         }
31158         // show showdown...
31159         this.updateMarkdown();
31160         this.markdownEl.removeClass('d-none');
31161         this.editing = false;
31162         return;
31163     },
31164     updateMarkdown : function()
31165     {
31166         if (this.getValue() == '') {
31167             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
31168             return;
31169         }
31170  
31171         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
31172     },
31173     
31174     resizeTextArea: function () {
31175         
31176         var sh = 100;
31177         Roo.log([sh, this.getValue().split("\n").length * 30]);
31178         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
31179     },
31180     setValue : function(val)
31181     {
31182         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
31183         if (!this.editing) {
31184             this.updateMarkdown();
31185         }
31186         
31187     },
31188     focus : function()
31189     {
31190         if (!this.editing) {
31191             this.toggleTextEdit();
31192         }
31193         
31194     }
31195
31196
31197 });/*
31198  * Based on:
31199  * Ext JS Library 1.1.1
31200  * Copyright(c) 2006-2007, Ext JS, LLC.
31201  *
31202  * Originally Released Under LGPL - original licence link has changed is not relivant.
31203  *
31204  * Fork - LGPL
31205  * <script type="text/javascript">
31206  */
31207  
31208 /**
31209  * @class Roo.bootstrap.PagingToolbar
31210  * @extends Roo.bootstrap.nav.Simplebar
31211  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31212  * @constructor
31213  * Create a new PagingToolbar
31214  * @param {Object} config The config object
31215  * @param {Roo.data.Store} store
31216  */
31217 Roo.bootstrap.PagingToolbar = function(config)
31218 {
31219     // old args format still supported... - xtype is prefered..
31220         // created from xtype...
31221     
31222     this.ds = config.dataSource;
31223     
31224     if (config.store && !this.ds) {
31225         this.store= Roo.factory(config.store, Roo.data);
31226         this.ds = this.store;
31227         this.ds.xmodule = this.xmodule || false;
31228     }
31229     
31230     this.toolbarItems = [];
31231     if (config.items) {
31232         this.toolbarItems = config.items;
31233     }
31234     
31235     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
31236     
31237     this.cursor = 0;
31238     
31239     if (this.ds) { 
31240         this.bind(this.ds);
31241     }
31242     
31243     if (Roo.bootstrap.version == 4) {
31244         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
31245     } else {
31246         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
31247     }
31248     
31249 };
31250
31251 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
31252     /**
31253      * @cfg {Roo.bootstrap.Button} buttons[]
31254      * Buttons for the toolbar
31255      */
31256      /**
31257      * @cfg {Roo.data.Store} store
31258      * The underlying data store providing the paged data
31259      */
31260     /**
31261      * @cfg {String/HTMLElement/Element} container
31262      * container The id or element that will contain the toolbar
31263      */
31264     /**
31265      * @cfg {Boolean} displayInfo
31266      * True to display the displayMsg (defaults to false)
31267      */
31268     /**
31269      * @cfg {Number} pageSize
31270      * The number of records to display per page (defaults to 20)
31271      */
31272     pageSize: 20,
31273     /**
31274      * @cfg {String} displayMsg
31275      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
31276      */
31277     displayMsg : 'Displaying {0} - {1} of {2}',
31278     /**
31279      * @cfg {String} emptyMsg
31280      * The message to display when no records are found (defaults to "No data to display")
31281      */
31282     emptyMsg : 'No data to display',
31283     /**
31284      * Customizable piece of the default paging text (defaults to "Page")
31285      * @type String
31286      */
31287     beforePageText : "Page",
31288     /**
31289      * Customizable piece of the default paging text (defaults to "of %0")
31290      * @type String
31291      */
31292     afterPageText : "of {0}",
31293     /**
31294      * Customizable piece of the default paging text (defaults to "First Page")
31295      * @type String
31296      */
31297     firstText : "First Page",
31298     /**
31299      * Customizable piece of the default paging text (defaults to "Previous Page")
31300      * @type String
31301      */
31302     prevText : "Previous Page",
31303     /**
31304      * Customizable piece of the default paging text (defaults to "Next Page")
31305      * @type String
31306      */
31307     nextText : "Next Page",
31308     /**
31309      * Customizable piece of the default paging text (defaults to "Last Page")
31310      * @type String
31311      */
31312     lastText : "Last Page",
31313     /**
31314      * Customizable piece of the default paging text (defaults to "Refresh")
31315      * @type String
31316      */
31317     refreshText : "Refresh",
31318
31319     buttons : false,
31320     // private
31321     onRender : function(ct, position) 
31322     {
31323         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
31324         this.navgroup.parentId = this.id;
31325         this.navgroup.onRender(this.el, null);
31326         // add the buttons to the navgroup
31327         
31328         if(this.displayInfo){
31329             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
31330             this.displayEl = this.el.select('.x-paging-info', true).first();
31331 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
31332 //            this.displayEl = navel.el.select('span',true).first();
31333         }
31334         
31335         var _this = this;
31336         
31337         if(this.buttons){
31338             Roo.each(_this.buttons, function(e){ // this might need to use render????
31339                Roo.factory(e).render(_this.el);
31340             });
31341         }
31342             
31343         Roo.each(_this.toolbarItems, function(e) {
31344             _this.navgroup.addItem(e);
31345         });
31346         
31347         
31348         this.first = this.navgroup.addItem({
31349             tooltip: this.firstText,
31350             cls: "prev btn-outline-secondary",
31351             html : ' <i class="fa fa-step-backward"></i>',
31352             disabled: true,
31353             preventDefault: true,
31354             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
31355         });
31356         
31357         this.prev =  this.navgroup.addItem({
31358             tooltip: this.prevText,
31359             cls: "prev btn-outline-secondary",
31360             html : ' <i class="fa fa-backward"></i>',
31361             disabled: true,
31362             preventDefault: true,
31363             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
31364         });
31365     //this.addSeparator();
31366         
31367         
31368         var field = this.navgroup.addItem( {
31369             tagtype : 'span',
31370             cls : 'x-paging-position  btn-outline-secondary',
31371              disabled: true,
31372             html : this.beforePageText  +
31373                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
31374                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
31375          } ); //?? escaped?
31376         
31377         this.field = field.el.select('input', true).first();
31378         this.field.on("keydown", this.onPagingKeydown, this);
31379         this.field.on("focus", function(){this.dom.select();});
31380     
31381     
31382         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
31383         //this.field.setHeight(18);
31384         //this.addSeparator();
31385         this.next = this.navgroup.addItem({
31386             tooltip: this.nextText,
31387             cls: "next btn-outline-secondary",
31388             html : ' <i class="fa fa-forward"></i>',
31389             disabled: true,
31390             preventDefault: true,
31391             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
31392         });
31393         this.last = this.navgroup.addItem({
31394             tooltip: this.lastText,
31395             html : ' <i class="fa fa-step-forward"></i>',
31396             cls: "next btn-outline-secondary",
31397             disabled: true,
31398             preventDefault: true,
31399             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
31400         });
31401     //this.addSeparator();
31402         this.loading = this.navgroup.addItem({
31403             tooltip: this.refreshText,
31404             cls: "btn-outline-secondary",
31405             html : ' <i class="fa fa-refresh"></i>',
31406             preventDefault: true,
31407             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
31408         });
31409         
31410     },
31411
31412     // private
31413     updateInfo : function(){
31414         if(this.displayEl){
31415             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
31416             var msg = count == 0 ?
31417                 this.emptyMsg :
31418                 String.format(
31419                     this.displayMsg,
31420                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
31421                 );
31422             this.displayEl.update(msg);
31423         }
31424     },
31425
31426     // private
31427     onLoad : function(ds, r, o)
31428     {
31429         this.cursor = o.params && o.params.start ? o.params.start : 0;
31430         
31431         var d = this.getPageData(),
31432             ap = d.activePage,
31433             ps = d.pages;
31434         
31435         
31436         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
31437         this.field.dom.value = ap;
31438         this.first.setDisabled(ap == 1);
31439         this.prev.setDisabled(ap == 1);
31440         this.next.setDisabled(ap == ps);
31441         this.last.setDisabled(ap == ps);
31442         this.loading.enable();
31443         this.updateInfo();
31444     },
31445
31446     // private
31447     getPageData : function(){
31448         var total = this.ds.getTotalCount();
31449         return {
31450             total : total,
31451             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
31452             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
31453         };
31454     },
31455
31456     // private
31457     onLoadError : function(proxy, o){
31458         this.loading.enable();
31459         if (this.ds.events.loadexception.listeners.length  < 2) {
31460             // nothing has been assigned to loadexception except this...
31461             // so 
31462             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
31463
31464         }
31465     },
31466
31467     // private
31468     onPagingKeydown : function(e){
31469         var k = e.getKey();
31470         var d = this.getPageData();
31471         if(k == e.RETURN){
31472             var v = this.field.dom.value, pageNum;
31473             if(!v || isNaN(pageNum = parseInt(v, 10))){
31474                 this.field.dom.value = d.activePage;
31475                 return;
31476             }
31477             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31478             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31479             e.stopEvent();
31480         }
31481         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))
31482         {
31483           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31484           this.field.dom.value = pageNum;
31485           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31486           e.stopEvent();
31487         }
31488         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31489         {
31490           var v = this.field.dom.value, pageNum; 
31491           var increment = (e.shiftKey) ? 10 : 1;
31492           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31493                 increment *= -1;
31494           }
31495           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31496             this.field.dom.value = d.activePage;
31497             return;
31498           }
31499           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31500           {
31501             this.field.dom.value = parseInt(v, 10) + increment;
31502             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31503             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31504           }
31505           e.stopEvent();
31506         }
31507     },
31508
31509     // private
31510     beforeLoad : function(){
31511         if(this.loading){
31512             this.loading.disable();
31513         }
31514     },
31515
31516     // private
31517     onClick : function(which){
31518         
31519         var ds = this.ds;
31520         if (!ds) {
31521             return;
31522         }
31523         
31524         switch(which){
31525             case "first":
31526                 ds.load({params:{start: 0, limit: this.pageSize}});
31527             break;
31528             case "prev":
31529                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31530             break;
31531             case "next":
31532                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31533             break;
31534             case "last":
31535                 var total = ds.getTotalCount();
31536                 var extra = total % this.pageSize;
31537                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31538                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31539             break;
31540             case "refresh":
31541                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31542             break;
31543         }
31544     },
31545
31546     /**
31547      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31548      * @param {Roo.data.Store} store The data store to unbind
31549      */
31550     unbind : function(ds){
31551         ds.un("beforeload", this.beforeLoad, this);
31552         ds.un("load", this.onLoad, this);
31553         ds.un("loadexception", this.onLoadError, this);
31554         ds.un("remove", this.updateInfo, this);
31555         ds.un("add", this.updateInfo, this);
31556         this.ds = undefined;
31557     },
31558
31559     /**
31560      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31561      * @param {Roo.data.Store} store The data store to bind
31562      */
31563     bind : function(ds){
31564         ds.on("beforeload", this.beforeLoad, this);
31565         ds.on("load", this.onLoad, this);
31566         ds.on("loadexception", this.onLoadError, this);
31567         ds.on("remove", this.updateInfo, this);
31568         ds.on("add", this.updateInfo, this);
31569         this.ds = ds;
31570     }
31571 });/*
31572  * - LGPL
31573  *
31574  * element
31575  * 
31576  */
31577
31578 /**
31579  * @class Roo.bootstrap.MessageBar
31580  * @extends Roo.bootstrap.Component
31581  * Bootstrap MessageBar class
31582  * @cfg {String} html contents of the MessageBar
31583  * @cfg {String} weight (info | success | warning | danger) default info
31584  * @cfg {String} beforeClass insert the bar before the given class
31585  * @cfg {Boolean} closable (true | false) default false
31586  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
31587  * 
31588  * @constructor
31589  * Create a new Element
31590  * @param {Object} config The config object
31591  */
31592
31593 Roo.bootstrap.MessageBar = function(config){
31594     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
31595 };
31596
31597 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
31598     
31599     html: '',
31600     weight: 'info',
31601     closable: false,
31602     fixed: false,
31603     beforeClass: 'bootstrap-sticky-wrap',
31604     
31605     getAutoCreate : function(){
31606         
31607         var cfg = {
31608             tag: 'div',
31609             cls: 'alert alert-dismissable alert-' + this.weight,
31610             cn: [
31611                 {
31612                     tag: 'span',
31613                     cls: 'message',
31614                     html: this.html || ''
31615                 }
31616             ]
31617         };
31618         
31619         if(this.fixed){
31620             cfg.cls += ' alert-messages-fixed';
31621         }
31622         
31623         if(this.closable){
31624             cfg.cn.push({
31625                 tag: 'button',
31626                 cls: 'close',
31627                 html: 'x'
31628             });
31629         }
31630         
31631         return cfg;
31632     },
31633     
31634     onRender : function(ct, position)
31635     {
31636         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
31637         
31638         if(!this.el){
31639             var cfg = Roo.apply({},  this.getAutoCreate());
31640             cfg.id = Roo.id();
31641             
31642             if (this.cls) {
31643                 cfg.cls += ' ' + this.cls;
31644             }
31645             if (this.style) {
31646                 cfg.style = this.style;
31647             }
31648             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
31649             
31650             this.el.setVisibilityMode(Roo.Element.DISPLAY);
31651         }
31652         
31653         this.el.select('>button.close').on('click', this.hide, this);
31654         
31655     },
31656     
31657     show : function()
31658     {
31659         if (!this.rendered) {
31660             this.render();
31661         }
31662         
31663         this.el.show();
31664         
31665         this.fireEvent('show', this);
31666         
31667     },
31668     
31669     hide : function()
31670     {
31671         if (!this.rendered) {
31672             this.render();
31673         }
31674         
31675         this.el.hide();
31676         
31677         this.fireEvent('hide', this);
31678     },
31679     
31680     update : function()
31681     {
31682 //        var e = this.el.dom.firstChild;
31683 //        
31684 //        if(this.closable){
31685 //            e = e.nextSibling;
31686 //        }
31687 //        
31688 //        e.data = this.html || '';
31689
31690         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
31691     }
31692    
31693 });
31694
31695  
31696
31697      /*
31698  * - LGPL
31699  *
31700  * Graph
31701  * 
31702  */
31703
31704
31705 /**
31706  * @class Roo.bootstrap.Graph
31707  * @extends Roo.bootstrap.Component
31708  * Bootstrap Graph class
31709 > Prameters
31710  -sm {number} sm 4
31711  -md {number} md 5
31712  @cfg {String} graphtype  bar | vbar | pie
31713  @cfg {number} g_x coodinator | centre x (pie)
31714  @cfg {number} g_y coodinator | centre y (pie)
31715  @cfg {number} g_r radius (pie)
31716  @cfg {number} g_height height of the chart (respected by all elements in the set)
31717  @cfg {number} g_width width of the chart (respected by all elements in the set)
31718  @cfg {Object} title The title of the chart
31719     
31720  -{Array}  values
31721  -opts (object) options for the chart 
31722      o {
31723      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
31724      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
31725      o vgutter (number)
31726      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.
31727      o stacked (boolean) whether or not to tread values as in a stacked bar chart
31728      o to
31729      o stretch (boolean)
31730      o }
31731  -opts (object) options for the pie
31732      o{
31733      o cut
31734      o startAngle (number)
31735      o endAngle (number)
31736      } 
31737  *
31738  * @constructor
31739  * Create a new Input
31740  * @param {Object} config The config object
31741  */
31742
31743 Roo.bootstrap.Graph = function(config){
31744     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
31745     
31746     this.addEvents({
31747         // img events
31748         /**
31749          * @event click
31750          * The img click event for the img.
31751          * @param {Roo.EventObject} e
31752          */
31753         "click" : true
31754     });
31755 };
31756
31757 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
31758     
31759     sm: 4,
31760     md: 5,
31761     graphtype: 'bar',
31762     g_height: 250,
31763     g_width: 400,
31764     g_x: 50,
31765     g_y: 50,
31766     g_r: 30,
31767     opts:{
31768         //g_colors: this.colors,
31769         g_type: 'soft',
31770         g_gutter: '20%'
31771
31772     },
31773     title : false,
31774
31775     getAutoCreate : function(){
31776         
31777         var cfg = {
31778             tag: 'div',
31779             html : null
31780         };
31781         
31782         
31783         return  cfg;
31784     },
31785
31786     onRender : function(ct,position){
31787         
31788         
31789         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
31790         
31791         if (typeof(Raphael) == 'undefined') {
31792             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
31793             return;
31794         }
31795         
31796         this.raphael = Raphael(this.el.dom);
31797         
31798                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31799                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31800                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31801                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
31802                 /*
31803                 r.text(160, 10, "Single Series Chart").attr(txtattr);
31804                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
31805                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
31806                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
31807                 
31808                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
31809                 r.barchart(330, 10, 300, 220, data1);
31810                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
31811                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
31812                 */
31813                 
31814                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
31815                 // r.barchart(30, 30, 560, 250,  xdata, {
31816                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
31817                 //     axis : "0 0 1 1",
31818                 //     axisxlabels :  xdata
31819                 //     //yvalues : cols,
31820                    
31821                 // });
31822 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
31823 //        
31824 //        this.load(null,xdata,{
31825 //                axis : "0 0 1 1",
31826 //                axisxlabels :  xdata
31827 //                });
31828
31829     },
31830
31831     load : function(graphtype,xdata,opts)
31832     {
31833         this.raphael.clear();
31834         if(!graphtype) {
31835             graphtype = this.graphtype;
31836         }
31837         if(!opts){
31838             opts = this.opts;
31839         }
31840         var r = this.raphael,
31841             fin = function () {
31842                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
31843             },
31844             fout = function () {
31845                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
31846             },
31847             pfin = function() {
31848                 this.sector.stop();
31849                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
31850
31851                 if (this.label) {
31852                     this.label[0].stop();
31853                     this.label[0].attr({ r: 7.5 });
31854                     this.label[1].attr({ "font-weight": 800 });
31855                 }
31856             },
31857             pfout = function() {
31858                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
31859
31860                 if (this.label) {
31861                     this.label[0].animate({ r: 5 }, 500, "bounce");
31862                     this.label[1].attr({ "font-weight": 400 });
31863                 }
31864             };
31865
31866         switch(graphtype){
31867             case 'bar':
31868                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
31869                 break;
31870             case 'hbar':
31871                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
31872                 break;
31873             case 'pie':
31874 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
31875 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
31876 //            
31877                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
31878                 
31879                 break;
31880
31881         }
31882         
31883         if(this.title){
31884             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
31885         }
31886         
31887     },
31888     
31889     setTitle: function(o)
31890     {
31891         this.title = o;
31892     },
31893     
31894     initEvents: function() {
31895         
31896         if(!this.href){
31897             this.el.on('click', this.onClick, this);
31898         }
31899     },
31900     
31901     onClick : function(e)
31902     {
31903         Roo.log('img onclick');
31904         this.fireEvent('click', this, e);
31905     }
31906    
31907 });
31908
31909  
31910 Roo.bootstrap.dash = {};/*
31911  * - LGPL
31912  *
31913  * numberBox
31914  * 
31915  */
31916 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
31917
31918 /**
31919  * @class Roo.bootstrap.dash.NumberBox
31920  * @extends Roo.bootstrap.Component
31921  * Bootstrap NumberBox class
31922  * @cfg {String} headline Box headline
31923  * @cfg {String} content Box content
31924  * @cfg {String} icon Box icon
31925  * @cfg {String} footer Footer text
31926  * @cfg {String} fhref Footer href
31927  * 
31928  * @constructor
31929  * Create a new NumberBox
31930  * @param {Object} config The config object
31931  */
31932
31933
31934 Roo.bootstrap.dash.NumberBox = function(config){
31935     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
31936     
31937 };
31938
31939 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
31940     
31941     headline : '',
31942     content : '',
31943     icon : '',
31944     footer : '',
31945     fhref : '',
31946     ficon : '',
31947     
31948     getAutoCreate : function(){
31949         
31950         var cfg = {
31951             tag : 'div',
31952             cls : 'small-box ',
31953             cn : [
31954                 {
31955                     tag : 'div',
31956                     cls : 'inner',
31957                     cn :[
31958                         {
31959                             tag : 'h3',
31960                             cls : 'roo-headline',
31961                             html : this.headline
31962                         },
31963                         {
31964                             tag : 'p',
31965                             cls : 'roo-content',
31966                             html : this.content
31967                         }
31968                     ]
31969                 }
31970             ]
31971         };
31972         
31973         if(this.icon){
31974             cfg.cn.push({
31975                 tag : 'div',
31976                 cls : 'icon',
31977                 cn :[
31978                     {
31979                         tag : 'i',
31980                         cls : 'ion ' + this.icon
31981                     }
31982                 ]
31983             });
31984         }
31985         
31986         if(this.footer){
31987             var footer = {
31988                 tag : 'a',
31989                 cls : 'small-box-footer',
31990                 href : this.fhref || '#',
31991                 html : this.footer
31992             };
31993             
31994             cfg.cn.push(footer);
31995             
31996         }
31997         
31998         return  cfg;
31999     },
32000
32001     onRender : function(ct,position){
32002         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
32003
32004
32005        
32006                 
32007     },
32008
32009     setHeadline: function (value)
32010     {
32011         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
32012     },
32013     
32014     setFooter: function (value, href)
32015     {
32016         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
32017         
32018         if(href){
32019             this.el.select('a.small-box-footer',true).first().attr('href', href);
32020         }
32021         
32022     },
32023
32024     setContent: function (value)
32025     {
32026         this.el.select('.roo-content',true).first().dom.innerHTML = value;
32027     },
32028
32029     initEvents: function() 
32030     {   
32031         
32032     }
32033     
32034 });
32035
32036  
32037 /*
32038  * - LGPL
32039  *
32040  * TabBox
32041  * 
32042  */
32043 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
32044
32045 /**
32046  * @class Roo.bootstrap.dash.TabBox
32047  * @extends Roo.bootstrap.Component
32048  * @children Roo.bootstrap.dash.TabPane
32049  * Bootstrap TabBox class
32050  * @cfg {String} title Title of the TabBox
32051  * @cfg {String} icon Icon of the TabBox
32052  * @cfg {Boolean} showtabs (true|false) show the tabs default true
32053  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
32054  * 
32055  * @constructor
32056  * Create a new TabBox
32057  * @param {Object} config The config object
32058  */
32059
32060
32061 Roo.bootstrap.dash.TabBox = function(config){
32062     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
32063     this.addEvents({
32064         // raw events
32065         /**
32066          * @event addpane
32067          * When a pane is added
32068          * @param {Roo.bootstrap.dash.TabPane} pane
32069          */
32070         "addpane" : true,
32071         /**
32072          * @event activatepane
32073          * When a pane is activated
32074          * @param {Roo.bootstrap.dash.TabPane} pane
32075          */
32076         "activatepane" : true
32077         
32078          
32079     });
32080     
32081     this.panes = [];
32082 };
32083
32084 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
32085
32086     title : '',
32087     icon : false,
32088     showtabs : true,
32089     tabScrollable : false,
32090     
32091     getChildContainer : function()
32092     {
32093         return this.el.select('.tab-content', true).first();
32094     },
32095     
32096     getAutoCreate : function(){
32097         
32098         var header = {
32099             tag: 'li',
32100             cls: 'pull-left header',
32101             html: this.title,
32102             cn : []
32103         };
32104         
32105         if(this.icon){
32106             header.cn.push({
32107                 tag: 'i',
32108                 cls: 'fa ' + this.icon
32109             });
32110         }
32111         
32112         var h = {
32113             tag: 'ul',
32114             cls: 'nav nav-tabs pull-right',
32115             cn: [
32116                 header
32117             ]
32118         };
32119         
32120         if(this.tabScrollable){
32121             h = {
32122                 tag: 'div',
32123                 cls: 'tab-header',
32124                 cn: [
32125                     {
32126                         tag: 'ul',
32127                         cls: 'nav nav-tabs pull-right',
32128                         cn: [
32129                             header
32130                         ]
32131                     }
32132                 ]
32133             };
32134         }
32135         
32136         var cfg = {
32137             tag: 'div',
32138             cls: 'nav-tabs-custom',
32139             cn: [
32140                 h,
32141                 {
32142                     tag: 'div',
32143                     cls: 'tab-content no-padding',
32144                     cn: []
32145                 }
32146             ]
32147         };
32148
32149         return  cfg;
32150     },
32151     initEvents : function()
32152     {
32153         //Roo.log('add add pane handler');
32154         this.on('addpane', this.onAddPane, this);
32155     },
32156      /**
32157      * Updates the box title
32158      * @param {String} html to set the title to.
32159      */
32160     setTitle : function(value)
32161     {
32162         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
32163     },
32164     onAddPane : function(pane)
32165     {
32166         this.panes.push(pane);
32167         //Roo.log('addpane');
32168         //Roo.log(pane);
32169         // tabs are rendere left to right..
32170         if(!this.showtabs){
32171             return;
32172         }
32173         
32174         var ctr = this.el.select('.nav-tabs', true).first();
32175          
32176          
32177         var existing = ctr.select('.nav-tab',true);
32178         var qty = existing.getCount();;
32179         
32180         
32181         var tab = ctr.createChild({
32182             tag : 'li',
32183             cls : 'nav-tab' + (qty ? '' : ' active'),
32184             cn : [
32185                 {
32186                     tag : 'a',
32187                     href:'#',
32188                     html : pane.title
32189                 }
32190             ]
32191         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
32192         pane.tab = tab;
32193         
32194         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
32195         if (!qty) {
32196             pane.el.addClass('active');
32197         }
32198         
32199                 
32200     },
32201     onTabClick : function(ev,un,ob,pane)
32202     {
32203         //Roo.log('tab - prev default');
32204         ev.preventDefault();
32205         
32206         
32207         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
32208         pane.tab.addClass('active');
32209         //Roo.log(pane.title);
32210         this.getChildContainer().select('.tab-pane',true).removeClass('active');
32211         // technically we should have a deactivate event.. but maybe add later.
32212         // and it should not de-activate the selected tab...
32213         this.fireEvent('activatepane', pane);
32214         pane.el.addClass('active');
32215         pane.fireEvent('activate');
32216         
32217         
32218     },
32219     
32220     getActivePane : function()
32221     {
32222         var r = false;
32223         Roo.each(this.panes, function(p) {
32224             if(p.el.hasClass('active')){
32225                 r = p;
32226                 return false;
32227             }
32228             
32229             return;
32230         });
32231         
32232         return r;
32233     }
32234     
32235     
32236 });
32237
32238  
32239 /*
32240  * - LGPL
32241  *
32242  * Tab pane
32243  * 
32244  */
32245 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
32246 /**
32247  * @class Roo.bootstrap.TabPane
32248  * @extends Roo.bootstrap.Component
32249  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
32250  * Bootstrap TabPane class
32251  * @cfg {Boolean} active (false | true) Default false
32252  * @cfg {String} title title of panel
32253
32254  * 
32255  * @constructor
32256  * Create a new TabPane
32257  * @param {Object} config The config object
32258  */
32259
32260 Roo.bootstrap.dash.TabPane = function(config){
32261     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
32262     
32263     this.addEvents({
32264         // raw events
32265         /**
32266          * @event activate
32267          * When a pane is activated
32268          * @param {Roo.bootstrap.dash.TabPane} pane
32269          */
32270         "activate" : true
32271          
32272     });
32273 };
32274
32275 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
32276     
32277     active : false,
32278     title : '',
32279     
32280     // the tabBox that this is attached to.
32281     tab : false,
32282      
32283     getAutoCreate : function() 
32284     {
32285         var cfg = {
32286             tag: 'div',
32287             cls: 'tab-pane'
32288         };
32289         
32290         if(this.active){
32291             cfg.cls += ' active';
32292         }
32293         
32294         return cfg;
32295     },
32296     initEvents  : function()
32297     {
32298         //Roo.log('trigger add pane handler');
32299         this.parent().fireEvent('addpane', this)
32300     },
32301     
32302      /**
32303      * Updates the tab title 
32304      * @param {String} html to set the title to.
32305      */
32306     setTitle: function(str)
32307     {
32308         if (!this.tab) {
32309             return;
32310         }
32311         this.title = str;
32312         this.tab.select('a', true).first().dom.innerHTML = str;
32313         
32314     }
32315     
32316     
32317     
32318 });
32319
32320  
32321
32322
32323  /*
32324  * - LGPL
32325  *
32326  * Tooltip
32327  * 
32328  */
32329
32330 /**
32331  * @class Roo.bootstrap.Tooltip
32332  * Bootstrap Tooltip class
32333  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
32334  * to determine which dom element triggers the tooltip.
32335  * 
32336  * It needs to add support for additional attributes like tooltip-position
32337  * 
32338  * @constructor
32339  * Create a new Toolti
32340  * @param {Object} config The config object
32341  */
32342
32343 Roo.bootstrap.Tooltip = function(config){
32344     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
32345     
32346     this.alignment = Roo.bootstrap.Tooltip.alignment;
32347     
32348     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
32349         this.alignment = config.alignment;
32350     }
32351     
32352 };
32353
32354 Roo.apply(Roo.bootstrap.Tooltip, {
32355     /**
32356      * @function init initialize tooltip monitoring.
32357      * @static
32358      */
32359     currentEl : false,
32360     currentTip : false,
32361     currentRegion : false,
32362     
32363     //  init : delay?
32364     
32365     init : function()
32366     {
32367         Roo.get(document).on('mouseover', this.enter ,this);
32368         Roo.get(document).on('mouseout', this.leave, this);
32369          
32370         
32371         this.currentTip = new Roo.bootstrap.Tooltip();
32372     },
32373     
32374     enter : function(ev)
32375     {
32376         var dom = ev.getTarget();
32377         
32378         //Roo.log(['enter',dom]);
32379         var el = Roo.fly(dom);
32380         if (this.currentEl) {
32381             //Roo.log(dom);
32382             //Roo.log(this.currentEl);
32383             //Roo.log(this.currentEl.contains(dom));
32384             if (this.currentEl == el) {
32385                 return;
32386             }
32387             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
32388                 return;
32389             }
32390
32391         }
32392         
32393         if (this.currentTip.el) {
32394             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
32395         }    
32396         //Roo.log(ev);
32397         
32398         if(!el || el.dom == document){
32399             return;
32400         }
32401         
32402         var bindEl = el; 
32403         var pel = false;
32404         if (!el.attr('tooltip')) {
32405             pel = el.findParent("[tooltip]");
32406             if (pel) {
32407                 bindEl = Roo.get(pel);
32408             }
32409         }
32410         
32411        
32412         
32413         // you can not look for children, as if el is the body.. then everythign is the child..
32414         if (!pel && !el.attr('tooltip')) { //
32415             if (!el.select("[tooltip]").elements.length) {
32416                 return;
32417             }
32418             // is the mouse over this child...?
32419             bindEl = el.select("[tooltip]").first();
32420             var xy = ev.getXY();
32421             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
32422                 //Roo.log("not in region.");
32423                 return;
32424             }
32425             //Roo.log("child element over..");
32426             
32427         }
32428         this.currentEl = el;
32429         this.currentTip.bind(bindEl);
32430         this.currentRegion = Roo.lib.Region.getRegion(dom);
32431         this.currentTip.enter();
32432         
32433     },
32434     leave : function(ev)
32435     {
32436         var dom = ev.getTarget();
32437         //Roo.log(['leave',dom]);
32438         if (!this.currentEl) {
32439             return;
32440         }
32441         
32442         
32443         if (dom != this.currentEl.dom) {
32444             return;
32445         }
32446         var xy = ev.getXY();
32447         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
32448             return;
32449         }
32450         // only activate leave if mouse cursor is outside... bounding box..
32451         
32452         
32453         
32454         
32455         if (this.currentTip) {
32456             this.currentTip.leave();
32457         }
32458         //Roo.log('clear currentEl');
32459         this.currentEl = false;
32460         
32461         
32462     },
32463     alignment : {
32464         'left' : ['r-l', [-2,0], 'right'],
32465         'right' : ['l-r', [2,0], 'left'],
32466         'bottom' : ['t-b', [0,2], 'top'],
32467         'top' : [ 'b-t', [0,-2], 'bottom']
32468     }
32469     
32470 });
32471
32472
32473 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
32474     
32475     
32476     bindEl : false,
32477     
32478     delay : null, // can be { show : 300 , hide: 500}
32479     
32480     timeout : null,
32481     
32482     hoverState : null, //???
32483     
32484     placement : 'bottom', 
32485     
32486     alignment : false,
32487     
32488     getAutoCreate : function(){
32489     
32490         var cfg = {
32491            cls : 'tooltip',   
32492            role : 'tooltip',
32493            cn : [
32494                 {
32495                     cls : 'tooltip-arrow arrow'
32496                 },
32497                 {
32498                     cls : 'tooltip-inner'
32499                 }
32500            ]
32501         };
32502         
32503         return cfg;
32504     },
32505     bind : function(el)
32506     {
32507         this.bindEl = el;
32508     },
32509     
32510     initEvents : function()
32511     {
32512         this.arrowEl = this.el.select('.arrow', true).first();
32513         this.innerEl = this.el.select('.tooltip-inner', true).first();
32514     },
32515     
32516     enter : function () {
32517        
32518         if (this.timeout != null) {
32519             clearTimeout(this.timeout);
32520         }
32521         
32522         this.hoverState = 'in';
32523          //Roo.log("enter - show");
32524         if (!this.delay || !this.delay.show) {
32525             this.show();
32526             return;
32527         }
32528         var _t = this;
32529         this.timeout = setTimeout(function () {
32530             if (_t.hoverState == 'in') {
32531                 _t.show();
32532             }
32533         }, this.delay.show);
32534     },
32535     leave : function()
32536     {
32537         clearTimeout(this.timeout);
32538     
32539         this.hoverState = 'out';
32540          if (!this.delay || !this.delay.hide) {
32541             this.hide();
32542             return;
32543         }
32544        
32545         var _t = this;
32546         this.timeout = setTimeout(function () {
32547             //Roo.log("leave - timeout");
32548             
32549             if (_t.hoverState == 'out') {
32550                 _t.hide();
32551                 Roo.bootstrap.Tooltip.currentEl = false;
32552             }
32553         }, delay);
32554     },
32555     
32556     show : function (msg)
32557     {
32558         if (!this.el) {
32559             this.render(document.body);
32560         }
32561         // set content.
32562         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
32563         
32564         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
32565         
32566         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
32567         
32568         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
32569                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
32570         
32571         var placement = typeof this.placement == 'function' ?
32572             this.placement.call(this, this.el, on_el) :
32573             this.placement;
32574             
32575         var autoToken = /\s?auto?\s?/i;
32576         var autoPlace = autoToken.test(placement);
32577         if (autoPlace) {
32578             placement = placement.replace(autoToken, '') || 'top';
32579         }
32580         
32581         //this.el.detach()
32582         //this.el.setXY([0,0]);
32583         this.el.show();
32584         //this.el.dom.style.display='block';
32585         
32586         //this.el.appendTo(on_el);
32587         
32588         var p = this.getPosition();
32589         var box = this.el.getBox();
32590         
32591         if (autoPlace) {
32592             // fixme..
32593         }
32594         
32595         var align = this.alignment[placement];
32596         
32597         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
32598         
32599         if(placement == 'top' || placement == 'bottom'){
32600             if(xy[0] < 0){
32601                 placement = 'right';
32602             }
32603             
32604             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
32605                 placement = 'left';
32606             }
32607             
32608             var scroll = Roo.select('body', true).first().getScroll();
32609             
32610             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
32611                 placement = 'top';
32612             }
32613             
32614             align = this.alignment[placement];
32615             
32616             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
32617             
32618         }
32619         
32620         var elems = document.getElementsByTagName('div');
32621         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
32622         for (var i = 0; i < elems.length; i++) {
32623           var zindex = Number.parseInt(
32624                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
32625                 10
32626           );
32627           if (zindex > highest) {
32628             highest = zindex;
32629           }
32630         }
32631         
32632         
32633         
32634         this.el.dom.style.zIndex = highest;
32635         
32636         this.el.alignTo(this.bindEl, align[0],align[1]);
32637         //var arrow = this.el.select('.arrow',true).first();
32638         //arrow.set(align[2], 
32639         
32640         this.el.addClass(placement);
32641         this.el.addClass("bs-tooltip-"+ placement);
32642         
32643         this.el.addClass('in fade show');
32644         
32645         this.hoverState = null;
32646         
32647         if (this.el.hasClass('fade')) {
32648             // fade it?
32649         }
32650         
32651         
32652         
32653         
32654         
32655     },
32656     hide : function()
32657     {
32658          
32659         if (!this.el) {
32660             return;
32661         }
32662         //this.el.setXY([0,0]);
32663         this.el.removeClass(['show', 'in']);
32664         //this.el.hide();
32665         
32666     }
32667     
32668 });
32669  
32670
32671  /*
32672  * - LGPL
32673  *
32674  * Location Picker
32675  * 
32676  */
32677
32678 /**
32679  * @class Roo.bootstrap.LocationPicker
32680  * @extends Roo.bootstrap.Component
32681  * Bootstrap LocationPicker class
32682  * @cfg {Number} latitude Position when init default 0
32683  * @cfg {Number} longitude Position when init default 0
32684  * @cfg {Number} zoom default 15
32685  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
32686  * @cfg {Boolean} mapTypeControl default false
32687  * @cfg {Boolean} disableDoubleClickZoom default false
32688  * @cfg {Boolean} scrollwheel default true
32689  * @cfg {Boolean} streetViewControl default false
32690  * @cfg {Number} radius default 0
32691  * @cfg {String} locationName
32692  * @cfg {Boolean} draggable default true
32693  * @cfg {Boolean} enableAutocomplete default false
32694  * @cfg {Boolean} enableReverseGeocode default true
32695  * @cfg {String} markerTitle
32696  * 
32697  * @constructor
32698  * Create a new LocationPicker
32699  * @param {Object} config The config object
32700  */
32701
32702
32703 Roo.bootstrap.LocationPicker = function(config){
32704     
32705     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
32706     
32707     this.addEvents({
32708         /**
32709          * @event initial
32710          * Fires when the picker initialized.
32711          * @param {Roo.bootstrap.LocationPicker} this
32712          * @param {Google Location} location
32713          */
32714         initial : true,
32715         /**
32716          * @event positionchanged
32717          * Fires when the picker position changed.
32718          * @param {Roo.bootstrap.LocationPicker} this
32719          * @param {Google Location} location
32720          */
32721         positionchanged : true,
32722         /**
32723          * @event resize
32724          * Fires when the map resize.
32725          * @param {Roo.bootstrap.LocationPicker} this
32726          */
32727         resize : true,
32728         /**
32729          * @event show
32730          * Fires when the map show.
32731          * @param {Roo.bootstrap.LocationPicker} this
32732          */
32733         show : true,
32734         /**
32735          * @event hide
32736          * Fires when the map hide.
32737          * @param {Roo.bootstrap.LocationPicker} this
32738          */
32739         hide : true,
32740         /**
32741          * @event mapClick
32742          * Fires when click the map.
32743          * @param {Roo.bootstrap.LocationPicker} this
32744          * @param {Map event} e
32745          */
32746         mapClick : true,
32747         /**
32748          * @event mapRightClick
32749          * Fires when right click the map.
32750          * @param {Roo.bootstrap.LocationPicker} this
32751          * @param {Map event} e
32752          */
32753         mapRightClick : true,
32754         /**
32755          * @event markerClick
32756          * Fires when click the marker.
32757          * @param {Roo.bootstrap.LocationPicker} this
32758          * @param {Map event} e
32759          */
32760         markerClick : true,
32761         /**
32762          * @event markerRightClick
32763          * Fires when right click the marker.
32764          * @param {Roo.bootstrap.LocationPicker} this
32765          * @param {Map event} e
32766          */
32767         markerRightClick : true,
32768         /**
32769          * @event OverlayViewDraw
32770          * Fires when OverlayView Draw
32771          * @param {Roo.bootstrap.LocationPicker} this
32772          */
32773         OverlayViewDraw : true,
32774         /**
32775          * @event OverlayViewOnAdd
32776          * Fires when OverlayView Draw
32777          * @param {Roo.bootstrap.LocationPicker} this
32778          */
32779         OverlayViewOnAdd : true,
32780         /**
32781          * @event OverlayViewOnRemove
32782          * Fires when OverlayView Draw
32783          * @param {Roo.bootstrap.LocationPicker} this
32784          */
32785         OverlayViewOnRemove : true,
32786         /**
32787          * @event OverlayViewShow
32788          * Fires when OverlayView Draw
32789          * @param {Roo.bootstrap.LocationPicker} this
32790          * @param {Pixel} cpx
32791          */
32792         OverlayViewShow : true,
32793         /**
32794          * @event OverlayViewHide
32795          * Fires when OverlayView Draw
32796          * @param {Roo.bootstrap.LocationPicker} this
32797          */
32798         OverlayViewHide : true,
32799         /**
32800          * @event loadexception
32801          * Fires when load google lib failed.
32802          * @param {Roo.bootstrap.LocationPicker} this
32803          */
32804         loadexception : true
32805     });
32806         
32807 };
32808
32809 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
32810     
32811     gMapContext: false,
32812     
32813     latitude: 0,
32814     longitude: 0,
32815     zoom: 15,
32816     mapTypeId: false,
32817     mapTypeControl: false,
32818     disableDoubleClickZoom: false,
32819     scrollwheel: true,
32820     streetViewControl: false,
32821     radius: 0,
32822     locationName: '',
32823     draggable: true,
32824     enableAutocomplete: false,
32825     enableReverseGeocode: true,
32826     markerTitle: '',
32827     
32828     getAutoCreate: function()
32829     {
32830
32831         var cfg = {
32832             tag: 'div',
32833             cls: 'roo-location-picker'
32834         };
32835         
32836         return cfg
32837     },
32838     
32839     initEvents: function(ct, position)
32840     {       
32841         if(!this.el.getWidth() || this.isApplied()){
32842             return;
32843         }
32844         
32845         this.el.setVisibilityMode(Roo.Element.DISPLAY);
32846         
32847         this.initial();
32848     },
32849     
32850     initial: function()
32851     {
32852         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
32853             this.fireEvent('loadexception', this);
32854             return;
32855         }
32856         
32857         if(!this.mapTypeId){
32858             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
32859         }
32860         
32861         this.gMapContext = this.GMapContext();
32862         
32863         this.initOverlayView();
32864         
32865         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
32866         
32867         var _this = this;
32868                 
32869         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
32870             _this.setPosition(_this.gMapContext.marker.position);
32871         });
32872         
32873         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
32874             _this.fireEvent('mapClick', this, event);
32875             
32876         });
32877
32878         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
32879             _this.fireEvent('mapRightClick', this, event);
32880             
32881         });
32882         
32883         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
32884             _this.fireEvent('markerClick', this, event);
32885             
32886         });
32887
32888         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
32889             _this.fireEvent('markerRightClick', this, event);
32890             
32891         });
32892         
32893         this.setPosition(this.gMapContext.location);
32894         
32895         this.fireEvent('initial', this, this.gMapContext.location);
32896     },
32897     
32898     initOverlayView: function()
32899     {
32900         var _this = this;
32901         
32902         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
32903             
32904             draw: function()
32905             {
32906                 _this.fireEvent('OverlayViewDraw', _this);
32907             },
32908             
32909             onAdd: function()
32910             {
32911                 _this.fireEvent('OverlayViewOnAdd', _this);
32912             },
32913             
32914             onRemove: function()
32915             {
32916                 _this.fireEvent('OverlayViewOnRemove', _this);
32917             },
32918             
32919             show: function(cpx)
32920             {
32921                 _this.fireEvent('OverlayViewShow', _this, cpx);
32922             },
32923             
32924             hide: function()
32925             {
32926                 _this.fireEvent('OverlayViewHide', _this);
32927             }
32928             
32929         });
32930     },
32931     
32932     fromLatLngToContainerPixel: function(event)
32933     {
32934         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
32935     },
32936     
32937     isApplied: function() 
32938     {
32939         return this.getGmapContext() == false ? false : true;
32940     },
32941     
32942     getGmapContext: function() 
32943     {
32944         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
32945     },
32946     
32947     GMapContext: function() 
32948     {
32949         var position = new google.maps.LatLng(this.latitude, this.longitude);
32950         
32951         var _map = new google.maps.Map(this.el.dom, {
32952             center: position,
32953             zoom: this.zoom,
32954             mapTypeId: this.mapTypeId,
32955             mapTypeControl: this.mapTypeControl,
32956             disableDoubleClickZoom: this.disableDoubleClickZoom,
32957             scrollwheel: this.scrollwheel,
32958             streetViewControl: this.streetViewControl,
32959             locationName: this.locationName,
32960             draggable: this.draggable,
32961             enableAutocomplete: this.enableAutocomplete,
32962             enableReverseGeocode: this.enableReverseGeocode
32963         });
32964         
32965         var _marker = new google.maps.Marker({
32966             position: position,
32967             map: _map,
32968             title: this.markerTitle,
32969             draggable: this.draggable
32970         });
32971         
32972         return {
32973             map: _map,
32974             marker: _marker,
32975             circle: null,
32976             location: position,
32977             radius: this.radius,
32978             locationName: this.locationName,
32979             addressComponents: {
32980                 formatted_address: null,
32981                 addressLine1: null,
32982                 addressLine2: null,
32983                 streetName: null,
32984                 streetNumber: null,
32985                 city: null,
32986                 district: null,
32987                 state: null,
32988                 stateOrProvince: null
32989             },
32990             settings: this,
32991             domContainer: this.el.dom,
32992             geodecoder: new google.maps.Geocoder()
32993         };
32994     },
32995     
32996     drawCircle: function(center, radius, options) 
32997     {
32998         if (this.gMapContext.circle != null) {
32999             this.gMapContext.circle.setMap(null);
33000         }
33001         if (radius > 0) {
33002             radius *= 1;
33003             options = Roo.apply({}, options, {
33004                 strokeColor: "#0000FF",
33005                 strokeOpacity: .35,
33006                 strokeWeight: 2,
33007                 fillColor: "#0000FF",
33008                 fillOpacity: .2
33009             });
33010             
33011             options.map = this.gMapContext.map;
33012             options.radius = radius;
33013             options.center = center;
33014             this.gMapContext.circle = new google.maps.Circle(options);
33015             return this.gMapContext.circle;
33016         }
33017         
33018         return null;
33019     },
33020     
33021     setPosition: function(location) 
33022     {
33023         this.gMapContext.location = location;
33024         this.gMapContext.marker.setPosition(location);
33025         this.gMapContext.map.panTo(location);
33026         this.drawCircle(location, this.gMapContext.radius, {});
33027         
33028         var _this = this;
33029         
33030         if (this.gMapContext.settings.enableReverseGeocode) {
33031             this.gMapContext.geodecoder.geocode({
33032                 latLng: this.gMapContext.location
33033             }, function(results, status) {
33034                 
33035                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
33036                     _this.gMapContext.locationName = results[0].formatted_address;
33037                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
33038                     
33039                     _this.fireEvent('positionchanged', this, location);
33040                 }
33041             });
33042             
33043             return;
33044         }
33045         
33046         this.fireEvent('positionchanged', this, location);
33047     },
33048     
33049     resize: function()
33050     {
33051         google.maps.event.trigger(this.gMapContext.map, "resize");
33052         
33053         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
33054         
33055         this.fireEvent('resize', this);
33056     },
33057     
33058     setPositionByLatLng: function(latitude, longitude)
33059     {
33060         this.setPosition(new google.maps.LatLng(latitude, longitude));
33061     },
33062     
33063     getCurrentPosition: function() 
33064     {
33065         return {
33066             latitude: this.gMapContext.location.lat(),
33067             longitude: this.gMapContext.location.lng()
33068         };
33069     },
33070     
33071     getAddressName: function() 
33072     {
33073         return this.gMapContext.locationName;
33074     },
33075     
33076     getAddressComponents: function() 
33077     {
33078         return this.gMapContext.addressComponents;
33079     },
33080     
33081     address_component_from_google_geocode: function(address_components) 
33082     {
33083         var result = {};
33084         
33085         for (var i = 0; i < address_components.length; i++) {
33086             var component = address_components[i];
33087             if (component.types.indexOf("postal_code") >= 0) {
33088                 result.postalCode = component.short_name;
33089             } else if (component.types.indexOf("street_number") >= 0) {
33090                 result.streetNumber = component.short_name;
33091             } else if (component.types.indexOf("route") >= 0) {
33092                 result.streetName = component.short_name;
33093             } else if (component.types.indexOf("neighborhood") >= 0) {
33094                 result.city = component.short_name;
33095             } else if (component.types.indexOf("locality") >= 0) {
33096                 result.city = component.short_name;
33097             } else if (component.types.indexOf("sublocality") >= 0) {
33098                 result.district = component.short_name;
33099             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
33100                 result.stateOrProvince = component.short_name;
33101             } else if (component.types.indexOf("country") >= 0) {
33102                 result.country = component.short_name;
33103             }
33104         }
33105         
33106         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
33107         result.addressLine2 = "";
33108         return result;
33109     },
33110     
33111     setZoomLevel: function(zoom)
33112     {
33113         this.gMapContext.map.setZoom(zoom);
33114     },
33115     
33116     show: function()
33117     {
33118         if(!this.el){
33119             return;
33120         }
33121         
33122         this.el.show();
33123         
33124         this.resize();
33125         
33126         this.fireEvent('show', this);
33127     },
33128     
33129     hide: function()
33130     {
33131         if(!this.el){
33132             return;
33133         }
33134         
33135         this.el.hide();
33136         
33137         this.fireEvent('hide', this);
33138     }
33139     
33140 });
33141
33142 Roo.apply(Roo.bootstrap.LocationPicker, {
33143     
33144     OverlayView : function(map, options)
33145     {
33146         options = options || {};
33147         
33148         this.setMap(map);
33149     }
33150     
33151     
33152 });/**
33153  * @class Roo.bootstrap.Alert
33154  * @extends Roo.bootstrap.Component
33155  * Bootstrap Alert class - shows an alert area box
33156  * eg
33157  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
33158   Enter a valid email address
33159 </div>
33160  * @licence LGPL
33161  * @cfg {String} title The title of alert
33162  * @cfg {String} html The content of alert
33163  * @cfg {String} weight (success|info|warning|danger) Weight of the message
33164  * @cfg {String} fa font-awesomeicon
33165  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
33166  * @cfg {Boolean} close true to show a x closer
33167  * 
33168  * 
33169  * @constructor
33170  * Create a new alert
33171  * @param {Object} config The config object
33172  */
33173
33174
33175 Roo.bootstrap.Alert = function(config){
33176     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
33177     
33178 };
33179
33180 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
33181     
33182     title: '',
33183     html: '',
33184     weight: false,
33185     fa: false,
33186     faicon: false, // BC
33187     close : false,
33188     
33189     
33190     getAutoCreate : function()
33191     {
33192         
33193         var cfg = {
33194             tag : 'div',
33195             cls : 'alert',
33196             cn : [
33197                 {
33198                     tag: 'button',
33199                     type :  "button",
33200                     cls: "close",
33201                     html : '×',
33202                     style : this.close ? '' : 'display:none'
33203                 },
33204                 {
33205                     tag : 'i',
33206                     cls : 'roo-alert-icon'
33207                     
33208                 },
33209                 {
33210                     tag : 'b',
33211                     cls : 'roo-alert-title',
33212                     html : this.title
33213                 },
33214                 {
33215                     tag : 'span',
33216                     cls : 'roo-alert-text',
33217                     html : this.html
33218                 }
33219             ]
33220         };
33221         
33222         if(this.faicon){
33223             cfg.cn[0].cls += ' fa ' + this.faicon;
33224         }
33225         if(this.fa){
33226             cfg.cn[0].cls += ' fa ' + this.fa;
33227         }
33228         
33229         if(this.weight){
33230             cfg.cls += ' alert-' + this.weight;
33231         }
33232         
33233         return cfg;
33234     },
33235     
33236     initEvents: function() 
33237     {
33238         this.el.setVisibilityMode(Roo.Element.DISPLAY);
33239         this.titleEl =  this.el.select('.roo-alert-title',true).first();
33240         this.iconEl = this.el.select('.roo-alert-icon',true).first();
33241         this.htmlEl = this.el.select('.roo-alert-text',true).first();
33242         if (this.seconds > 0) {
33243             this.hide.defer(this.seconds, this);
33244         }
33245     },
33246     /**
33247      * Set the Title Message HTML
33248      * @param {String} html
33249      */
33250     setTitle : function(str)
33251     {
33252         this.titleEl.dom.innerHTML = str;
33253     },
33254      
33255      /**
33256      * Set the Body Message HTML
33257      * @param {String} html
33258      */
33259     setHtml : function(str)
33260     {
33261         this.htmlEl.dom.innerHTML = str;
33262     },
33263     /**
33264      * Set the Weight of the alert
33265      * @param {String} (success|info|warning|danger) weight
33266      */
33267     
33268     setWeight : function(weight)
33269     {
33270         if(this.weight){
33271             this.el.removeClass('alert-' + this.weight);
33272         }
33273         
33274         this.weight = weight;
33275         
33276         this.el.addClass('alert-' + this.weight);
33277     },
33278       /**
33279      * Set the Icon of the alert
33280      * @param {String} see fontawsome names (name without the 'fa-' bit)
33281      */
33282     setIcon : function(icon)
33283     {
33284         if(this.faicon){
33285             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
33286         }
33287         
33288         this.faicon = icon;
33289         
33290         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
33291     },
33292     /**
33293      * Hide the Alert
33294      */
33295     hide: function() 
33296     {
33297         this.el.hide();   
33298     },
33299     /**
33300      * Show the Alert
33301      */
33302     show: function() 
33303     {  
33304         this.el.show();   
33305     }
33306     
33307 });
33308
33309  
33310 /*
33311 * Licence: LGPL
33312 */
33313
33314 /**
33315  * @class Roo.bootstrap.UploadCropbox
33316  * @extends Roo.bootstrap.Component
33317  * Bootstrap UploadCropbox class
33318  * @cfg {String} emptyText show when image has been loaded
33319  * @cfg {String} rotateNotify show when image too small to rotate
33320  * @cfg {Number} errorTimeout default 3000
33321  * @cfg {Number} minWidth default 300
33322  * @cfg {Number} minHeight default 300
33323  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
33324  * @cfg {Boolean} isDocument (true|false) default false
33325  * @cfg {String} url action url
33326  * @cfg {String} paramName default 'imageUpload'
33327  * @cfg {String} method default POST
33328  * @cfg {Boolean} loadMask (true|false) default true
33329  * @cfg {Boolean} loadingText default 'Loading...'
33330  * 
33331  * @constructor
33332  * Create a new UploadCropbox
33333  * @param {Object} config The config object
33334  */
33335
33336 Roo.bootstrap.UploadCropbox = function(config){
33337     console.log("BOOTSTRAP UPLOAD CROPBOX CONSTRUCTOR");
33338     console.log(config);
33339     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
33340     
33341     this.addEvents({
33342         /**
33343          * @event beforeselectfile
33344          * Fire before select file
33345          * @param {Roo.bootstrap.UploadCropbox} this
33346          */
33347         "beforeselectfile" : true,
33348         /**
33349          * @event initial
33350          * Fire after initEvent
33351          * @param {Roo.bootstrap.UploadCropbox} this
33352          */
33353         "initial" : true,
33354         /**
33355          * @event crop
33356          * Fire after initEvent
33357          * @param {Roo.bootstrap.UploadCropbox} this
33358          * @param {String} data
33359          */
33360         "crop" : true,
33361         /**
33362          * @event prepare
33363          * Fire when preparing the file data
33364          * @param {Roo.bootstrap.UploadCropbox} this
33365          * @param {Object} file
33366          */
33367         "prepare" : true,
33368         /**
33369          * @event exception
33370          * Fire when get exception
33371          * @param {Roo.bootstrap.UploadCropbox} this
33372          * @param {XMLHttpRequest} xhr
33373          */
33374         "exception" : true,
33375         /**
33376          * @event beforeloadcanvas
33377          * Fire before load the canvas
33378          * @param {Roo.bootstrap.UploadCropbox} this
33379          * @param {String} src
33380          */
33381         "beforeloadcanvas" : true,
33382         /**
33383          * @event trash
33384          * Fire when trash image
33385          * @param {Roo.bootstrap.UploadCropbox} this
33386          */
33387         "trash" : true,
33388         /**
33389          * @event download
33390          * Fire when download the image
33391          * @param {Roo.bootstrap.UploadCropbox} this
33392          */
33393         "download" : true,
33394         /**
33395          * @event footerbuttonclick
33396          * Fire when footerbuttonclick
33397          * @param {Roo.bootstrap.UploadCropbox} this
33398          * @param {String} type
33399          */
33400         "footerbuttonclick" : true,
33401         /**
33402          * @event resize
33403          * Fire when resize
33404          * @param {Roo.bootstrap.UploadCropbox} this
33405          */
33406         "resize" : true,
33407         /**
33408          * @event rotate
33409          * Fire when rotate the image
33410          * @param {Roo.bootstrap.UploadCropbox} this
33411          * @param {String} pos
33412          */
33413         "rotate" : true,
33414         /**
33415          * @event inspect
33416          * Fire when inspect the file
33417          * @param {Roo.bootstrap.UploadCropbox} this
33418          * @param {Object} file
33419          */
33420         "inspect" : true,
33421         /**
33422          * @event upload
33423          * Fire when xhr upload the file
33424          * @param {Roo.bootstrap.UploadCropbox} this
33425          * @param {Object} data
33426          */
33427         "upload" : true,
33428         /**
33429          * @event arrange
33430          * Fire when arrange the file data
33431          * @param {Roo.bootstrap.UploadCropbox} this
33432          * @param {Object} formData
33433          */
33434         "arrange" : true
33435     });
33436     
33437     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
33438 };
33439
33440 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
33441     
33442     emptyText : 'Click to upload image',
33443     rotateNotify : 'Image is too small to rotate',
33444     errorTimeout : 3000,
33445     scale : 0,
33446     baseScale : 1,
33447     rotate : 0,
33448     dragable : false,
33449     pinching : false,
33450     mouseX : 0,
33451     mouseY : 0,
33452     cropData : false,
33453     minWidth : 300,
33454     minHeight : 300,
33455     file : false,
33456     exif : {},
33457     baseRotate : 1,
33458     cropType : 'image/jpeg',
33459     buttons : false,
33460     canvasLoaded : false,
33461     isDocument : false,
33462     method : 'POST',
33463     paramName : 'imageUpload',
33464     loadMask : true,
33465     loadingText : 'Loading...',
33466     maskEl : false,
33467     
33468     getAutoCreate : function()
33469     {
33470         var cfg = {
33471             tag : 'div',
33472             cls : 'roo-upload-cropbox',
33473             cn : [
33474                 {
33475                     tag : 'input',
33476                     cls : 'roo-upload-cropbox-selector',
33477                     type : 'file'
33478                 },
33479                 {
33480                     tag : 'div',
33481                     cls : 'roo-upload-cropbox-body',
33482                     style : 'cursor:pointer',
33483                     cn : [
33484                         {
33485                             tag : 'div',
33486                             cls : 'roo-upload-cropbox-preview'
33487                         },
33488                         {
33489                             tag : 'div',
33490                             cls : 'roo-upload-cropbox-thumb'
33491                         },
33492                         {
33493                             tag : 'div',
33494                             cls : 'roo-upload-cropbox-empty-notify',
33495                             html : this.emptyText
33496                         },
33497                         {
33498                             tag : 'div',
33499                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
33500                             html : this.rotateNotify
33501                         }
33502                     ]
33503                 },
33504                 {
33505                     tag : 'div',
33506                     cls : 'roo-upload-cropbox-footer',
33507                     cn : {
33508                         tag : 'div',
33509                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
33510                         cn : []
33511                     }
33512                 }
33513             ]
33514         };
33515         
33516         return cfg;
33517     },
33518     
33519     onRender : function(ct, position)
33520     {
33521         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
33522         
33523         if (this.buttons.length) {
33524             
33525             Roo.each(this.buttons, function(bb) {
33526                 
33527                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
33528                 
33529                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
33530                 
33531             }, this);
33532         }
33533         
33534         if(this.loadMask){
33535             this.maskEl = this.el;
33536         }
33537     },
33538     
33539     initEvents : function()
33540     {
33541         this.urlAPI = (window.createObjectURL && window) || 
33542                                 (window.URL && URL.revokeObjectURL && URL) || 
33543                                 (window.webkitURL && webkitURL);
33544                         
33545         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
33546         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33547         
33548         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
33549         this.selectorEl.hide();
33550         
33551         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
33552         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33553         
33554         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
33555         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33556         this.thumbEl.hide();
33557         
33558         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
33559         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33560         
33561         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
33562         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33563         this.errorEl.hide();
33564         
33565         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
33566         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33567         this.footerEl.hide();
33568         
33569         this.setThumbBoxSize();
33570         
33571         this.bind();
33572         
33573         this.resize();
33574         
33575         this.fireEvent('initial', this);
33576     },
33577
33578     bind : function()
33579     {
33580         var _this = this;
33581         
33582         window.addEventListener("resize", function() { _this.resize(); } );
33583         
33584         this.bodyEl.on('click', this.beforeSelectFile, this);
33585         
33586         if(Roo.isTouch){
33587             this.bodyEl.on('touchstart', this.onTouchStart, this);
33588             this.bodyEl.on('touchmove', this.onTouchMove, this);
33589             this.bodyEl.on('touchend', this.onTouchEnd, this);
33590         }
33591         
33592         if(!Roo.isTouch){
33593             this.bodyEl.on('mousedown', this.onMouseDown, this);
33594             this.bodyEl.on('mousemove', this.onMouseMove, this);
33595             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
33596             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
33597             Roo.get(document).on('mouseup', this.onMouseUp, this);
33598         }
33599         
33600         this.selectorEl.on('change', this.onFileSelected, this);
33601     },
33602     
33603     reset : function()
33604     {    
33605         this.scale = 0;
33606         this.baseScale = 1;
33607         this.rotate = 0;
33608         this.baseRotate = 1;
33609         this.dragable = false;
33610         this.pinching = false;
33611         this.mouseX = 0;
33612         this.mouseY = 0;
33613         this.cropData = false;
33614         this.notifyEl.dom.innerHTML = this.emptyText;
33615         
33616         this.selectorEl.dom.value = '';
33617         
33618     },
33619     
33620     resize : function()
33621     {
33622         if(this.fireEvent('resize', this) != false){
33623             this.setThumbBoxPosition();
33624             this.setCanvasPosition();
33625         }
33626     },
33627     
33628     onFooterButtonClick : function(e, el, o, type)
33629     {
33630         switch (type) {
33631             case 'rotate-left' :
33632                 this.onRotateLeft(e);
33633                 break;
33634             case 'rotate-right' :
33635                 this.onRotateRight(e);
33636                 break;
33637             case 'picture' :
33638                 this.beforeSelectFile(e);
33639                 break;
33640             case 'trash' :
33641                 this.trash(e);
33642                 break;
33643             case 'crop' :
33644                 this.crop(e);
33645                 break;
33646             case 'download' :
33647                 this.download(e);
33648                 break;
33649             default :
33650                 break;
33651         }
33652         
33653         this.fireEvent('footerbuttonclick', this, type);
33654     },
33655     
33656     beforeSelectFile : function(e)
33657     {
33658         e.preventDefault();
33659         
33660         if(this.fireEvent('beforeselectfile', this) != false){
33661             this.selectorEl.dom.click();
33662         }
33663     },
33664     
33665     onFileSelected : function(e)
33666     {
33667         e.preventDefault();
33668         
33669         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
33670             return;
33671         }
33672         
33673         var file = this.selectorEl.dom.files[0];
33674         
33675         if(this.fireEvent('inspect', this, file) != false){
33676             this.prepare(file);
33677         }
33678         
33679     },
33680     
33681     trash : function(e)
33682     {
33683         this.fireEvent('trash', this);
33684     },
33685     
33686     download : function(e)
33687     {
33688         this.fireEvent('download', this);
33689     },
33690     
33691     loadCanvas : function(src)
33692     {   
33693         if(this.fireEvent('beforeloadcanvas', this, src) != false){
33694             
33695             this.reset();
33696             
33697             this.imageEl = document.createElement('img');
33698             
33699             var _this = this;
33700             
33701             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
33702             
33703             this.imageEl.src = src;
33704         }
33705     },
33706     
33707     onLoadCanvas : function()
33708     {   
33709         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
33710         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
33711         
33712         this.bodyEl.un('click', this.beforeSelectFile, this);
33713         
33714         this.notifyEl.hide();
33715         this.thumbEl.show();
33716         this.footerEl.show();
33717         
33718         this.baseRotateLevel();
33719         
33720         if(this.isDocument){
33721             this.setThumbBoxSize();
33722         }
33723         
33724         this.setThumbBoxPosition();
33725         
33726         this.baseScaleLevel();
33727         
33728         this.draw();
33729         
33730         this.resize();
33731         
33732         this.canvasLoaded = true;
33733         
33734         if(this.loadMask){
33735             this.maskEl.unmask();
33736         }
33737         
33738     },
33739     
33740     setCanvasPosition : function()
33741     {   
33742         if(!this.canvasEl){
33743             return;
33744         }
33745         
33746         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
33747         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
33748         
33749         this.previewEl.setLeft(pw);
33750         this.previewEl.setTop(ph);
33751         
33752     },
33753     
33754     onMouseDown : function(e)
33755     {   
33756         e.stopEvent();
33757         
33758         this.dragable = true;
33759         this.pinching = false;
33760         
33761         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
33762             this.dragable = false;
33763             return;
33764         }
33765         
33766         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33767         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33768         
33769     },
33770     
33771     onMouseMove : function(e)
33772     {   
33773         e.stopEvent();
33774         
33775         if(!this.canvasLoaded){
33776             return;
33777         }
33778         
33779         if (!this.dragable){
33780             return;
33781         }
33782         
33783         var minX = Math.ceil(this.thumbEl.getLeft(true));
33784         var minY = Math.ceil(this.thumbEl.getTop(true));
33785         
33786         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
33787         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
33788         
33789         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33790         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33791         
33792         x = x - this.mouseX;
33793         y = y - this.mouseY;
33794         
33795         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
33796         var bgY = Math.ceil(y + this.previewEl.getTop(true));
33797         
33798         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
33799         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
33800         
33801         this.previewEl.setLeft(bgX);
33802         this.previewEl.setTop(bgY);
33803         
33804         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33805         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33806     },
33807     
33808     onMouseUp : function(e)
33809     {   
33810         e.stopEvent();
33811         
33812         this.dragable = false;
33813     },
33814     
33815     onMouseWheel : function(e)
33816     {   
33817         e.stopEvent();
33818         
33819         this.startScale = this.scale;
33820         
33821         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
33822         
33823         if(!this.zoomable()){
33824             this.scale = this.startScale;
33825             return;
33826         }
33827         
33828         this.draw();
33829         
33830         return;
33831     },
33832     
33833     zoomable : function()
33834     {
33835         var minScale = this.thumbEl.getWidth() / this.minWidth;
33836         
33837         if(this.minWidth < this.minHeight){
33838             minScale = this.thumbEl.getHeight() / this.minHeight;
33839         }
33840         
33841         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
33842         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
33843         
33844         if(
33845                 this.isDocument &&
33846                 (this.rotate == 0 || this.rotate == 180) && 
33847                 (
33848                     width > this.imageEl.OriginWidth || 
33849                     height > this.imageEl.OriginHeight ||
33850                     (width < this.minWidth && height < this.minHeight)
33851                 )
33852         ){
33853             return false;
33854         }
33855         
33856         if(
33857                 this.isDocument &&
33858                 (this.rotate == 90 || this.rotate == 270) && 
33859                 (
33860                     width > this.imageEl.OriginWidth || 
33861                     height > this.imageEl.OriginHeight ||
33862                     (width < this.minHeight && height < this.minWidth)
33863                 )
33864         ){
33865             return false;
33866         }
33867         
33868         if(
33869                 !this.isDocument &&
33870                 (this.rotate == 0 || this.rotate == 180) && 
33871                 (
33872                     width < this.minWidth || 
33873                     width > this.imageEl.OriginWidth || 
33874                     height < this.minHeight || 
33875                     height > this.imageEl.OriginHeight
33876                 )
33877         ){
33878             return false;
33879         }
33880         
33881         if(
33882                 !this.isDocument &&
33883                 (this.rotate == 90 || this.rotate == 270) && 
33884                 (
33885                     width < this.minHeight || 
33886                     width > this.imageEl.OriginWidth || 
33887                     height < this.minWidth || 
33888                     height > this.imageEl.OriginHeight
33889                 )
33890         ){
33891             return false;
33892         }
33893         
33894         return true;
33895         
33896     },
33897     
33898     onRotateLeft : function(e)
33899     {   
33900         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
33901             
33902             var minScale = this.thumbEl.getWidth() / this.minWidth;
33903             
33904             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
33905             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
33906             
33907             this.startScale = this.scale;
33908             
33909             while (this.getScaleLevel() < minScale){
33910             
33911                 this.scale = this.scale + 1;
33912                 
33913                 if(!this.zoomable()){
33914                     break;
33915                 }
33916                 
33917                 if(
33918                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
33919                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
33920                 ){
33921                     continue;
33922                 }
33923                 
33924                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
33925
33926                 this.draw();
33927                 
33928                 return;
33929             }
33930             
33931             this.scale = this.startScale;
33932             
33933             this.onRotateFail();
33934             
33935             return false;
33936         }
33937         
33938         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
33939
33940         if(this.isDocument){
33941             this.setThumbBoxSize();
33942             this.setThumbBoxPosition();
33943             this.setCanvasPosition();
33944         }
33945         
33946         this.draw();
33947         
33948         this.fireEvent('rotate', this, 'left');
33949         
33950     },
33951     
33952     onRotateRight : function(e)
33953     {
33954         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
33955             
33956             var minScale = this.thumbEl.getWidth() / this.minWidth;
33957         
33958             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
33959             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
33960             
33961             this.startScale = this.scale;
33962             
33963             while (this.getScaleLevel() < minScale){
33964             
33965                 this.scale = this.scale + 1;
33966                 
33967                 if(!this.zoomable()){
33968                     break;
33969                 }
33970                 
33971                 if(
33972                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
33973                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
33974                 ){
33975                     continue;
33976                 }
33977                 
33978                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
33979
33980                 this.draw();
33981                 
33982                 return;
33983             }
33984             
33985             this.scale = this.startScale;
33986             
33987             this.onRotateFail();
33988             
33989             return false;
33990         }
33991         
33992         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
33993
33994         if(this.isDocument){
33995             this.setThumbBoxSize();
33996             this.setThumbBoxPosition();
33997             this.setCanvasPosition();
33998         }
33999         
34000         this.draw();
34001         
34002         this.fireEvent('rotate', this, 'right');
34003     },
34004     
34005     onRotateFail : function()
34006     {
34007         this.errorEl.show(true);
34008         
34009         var _this = this;
34010         
34011         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
34012     },
34013     
34014     draw : function()
34015     {
34016         this.previewEl.dom.innerHTML = '';
34017         
34018         var canvasEl = document.createElement("canvas");
34019         
34020         var contextEl = canvasEl.getContext("2d");
34021         
34022         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34023         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34024         var center = this.imageEl.OriginWidth / 2;
34025         
34026         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
34027             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34028             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34029             center = this.imageEl.OriginHeight / 2;
34030         }
34031         
34032         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
34033         
34034         contextEl.translate(center, center);
34035         contextEl.rotate(this.rotate * Math.PI / 180);
34036
34037         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34038         
34039         this.canvasEl = document.createElement("canvas");
34040         
34041         this.contextEl = this.canvasEl.getContext("2d");
34042         
34043         switch (this.rotate) {
34044             case 0 :
34045                 
34046                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34047                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34048                 
34049                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34050                 
34051                 break;
34052             case 90 : 
34053                 
34054                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34055                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34056                 
34057                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34058                     this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34059                     break;
34060                 }
34061                 
34062                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34063                 
34064                 break;
34065             case 180 :
34066                 
34067                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34068                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34069                 
34070                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34071                     this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34072                     break;
34073                 }
34074                 
34075                 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34076                 
34077                 break;
34078             case 270 :
34079                 
34080                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34081                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34082         
34083                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34084                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34085                     break;
34086                 }
34087                 
34088                 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34089                 
34090                 break;
34091             default : 
34092                 break;
34093         }
34094         
34095         this.previewEl.appendChild(this.canvasEl);
34096         
34097         this.setCanvasPosition();
34098     },
34099     
34100     crop : function()
34101     {
34102         if(!this.canvasLoaded){
34103             return;
34104         }
34105         
34106         var imageCanvas = document.createElement("canvas");
34107         
34108         var imageContext = imageCanvas.getContext("2d");
34109         
34110         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34111         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34112         
34113         var center = imageCanvas.width / 2;
34114         
34115         imageContext.translate(center, center);
34116         
34117         imageContext.rotate(this.rotate * Math.PI / 180);
34118         
34119         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34120         
34121         var canvas = document.createElement("canvas");
34122         
34123         var context = canvas.getContext("2d");
34124                 
34125         canvas.width = this.minWidth;
34126         canvas.height = this.minHeight;
34127
34128         switch (this.rotate) {
34129             case 0 :
34130                 
34131                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34132                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34133                 
34134                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34135                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34136                 
34137                 var targetWidth = this.minWidth - 2 * x;
34138                 var targetHeight = this.minHeight - 2 * y;
34139                 
34140                 var scale = 1;
34141                 
34142                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34143                     scale = targetWidth / width;
34144                 }
34145                 
34146                 if(x > 0 && y == 0){
34147                     scale = targetHeight / height;
34148                 }
34149                 
34150                 if(x > 0 && y > 0){
34151                     scale = targetWidth / width;
34152                     
34153                     if(width < height){
34154                         scale = targetHeight / height;
34155                     }
34156                 }
34157                 
34158                 context.scale(scale, scale);
34159                 
34160                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34161                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34162
34163                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34164                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34165
34166                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34167                 
34168                 break;
34169             case 90 : 
34170                 
34171                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34172                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34173                 
34174                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34175                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34176                 
34177                 var targetWidth = this.minWidth - 2 * x;
34178                 var targetHeight = this.minHeight - 2 * y;
34179                 
34180                 var scale = 1;
34181                 
34182                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34183                     scale = targetWidth / width;
34184                 }
34185                 
34186                 if(x > 0 && y == 0){
34187                     scale = targetHeight / height;
34188                 }
34189                 
34190                 if(x > 0 && y > 0){
34191                     scale = targetWidth / width;
34192                     
34193                     if(width < height){
34194                         scale = targetHeight / height;
34195                     }
34196                 }
34197                 
34198                 context.scale(scale, scale);
34199                 
34200                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34201                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34202
34203                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34204                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34205                 
34206                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34207                 
34208                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34209                 
34210                 break;
34211             case 180 :
34212                 
34213                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34214                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34215                 
34216                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34217                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34218                 
34219                 var targetWidth = this.minWidth - 2 * x;
34220                 var targetHeight = this.minHeight - 2 * y;
34221                 
34222                 var scale = 1;
34223                 
34224                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34225                     scale = targetWidth / width;
34226                 }
34227                 
34228                 if(x > 0 && y == 0){
34229                     scale = targetHeight / height;
34230                 }
34231                 
34232                 if(x > 0 && y > 0){
34233                     scale = targetWidth / width;
34234                     
34235                     if(width < height){
34236                         scale = targetHeight / height;
34237                     }
34238                 }
34239                 
34240                 context.scale(scale, scale);
34241                 
34242                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34243                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34244
34245                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34246                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34247
34248                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34249                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34250                 
34251                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34252                 
34253                 break;
34254             case 270 :
34255                 
34256                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34257                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34258                 
34259                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34260                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34261                 
34262                 var targetWidth = this.minWidth - 2 * x;
34263                 var targetHeight = this.minHeight - 2 * y;
34264                 
34265                 var scale = 1;
34266                 
34267                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34268                     scale = targetWidth / width;
34269                 }
34270                 
34271                 if(x > 0 && y == 0){
34272                     scale = targetHeight / height;
34273                 }
34274                 
34275                 if(x > 0 && y > 0){
34276                     scale = targetWidth / width;
34277                     
34278                     if(width < height){
34279                         scale = targetHeight / height;
34280                     }
34281                 }
34282                 
34283                 context.scale(scale, scale);
34284                 
34285                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34286                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34287
34288                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34289                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34290                 
34291                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34292                 
34293                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34294                 
34295                 break;
34296             default : 
34297                 break;
34298         }
34299         
34300         this.cropData = canvas.toDataURL(this.cropType);
34301         
34302         if(this.fireEvent('crop', this, this.cropData) !== false){
34303             this.process(this.file, this.cropData);
34304         }
34305         
34306         return;
34307         
34308     },
34309     
34310     setThumbBoxSize : function()
34311     {
34312         var width, height;
34313         
34314         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
34315             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
34316             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
34317             
34318             this.minWidth = width;
34319             this.minHeight = height;
34320             
34321             if(this.rotate == 90 || this.rotate == 270){
34322                 this.minWidth = height;
34323                 this.minHeight = width;
34324             }
34325         }
34326         
34327         height = 300;
34328         width = Math.ceil(this.minWidth * height / this.minHeight);
34329         
34330         if(this.minWidth > this.minHeight){
34331             width = 300;
34332             height = Math.ceil(this.minHeight * width / this.minWidth);
34333         }
34334         
34335         this.thumbEl.setStyle({
34336             width : width + 'px',
34337             height : height + 'px'
34338         });
34339
34340         return;
34341             
34342     },
34343     
34344     setThumbBoxPosition : function()
34345     {
34346         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
34347         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
34348         
34349         this.thumbEl.setLeft(x);
34350         this.thumbEl.setTop(y);
34351         
34352     },
34353     
34354     baseRotateLevel : function()
34355     {
34356         this.baseRotate = 1;
34357         
34358         if(
34359                 typeof(this.exif) != 'undefined' &&
34360                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
34361                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
34362         ){
34363             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
34364         }
34365         
34366         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
34367         
34368     },
34369     
34370     baseScaleLevel : function()
34371     {
34372         var width, height;
34373         
34374         if(this.isDocument){
34375             
34376             if(this.baseRotate == 6 || this.baseRotate == 8){
34377             
34378                 height = this.thumbEl.getHeight();
34379                 this.baseScale = height / this.imageEl.OriginWidth;
34380
34381                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
34382                     width = this.thumbEl.getWidth();
34383                     this.baseScale = width / this.imageEl.OriginHeight;
34384                 }
34385
34386                 return;
34387             }
34388
34389             height = this.thumbEl.getHeight();
34390             this.baseScale = height / this.imageEl.OriginHeight;
34391
34392             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
34393                 width = this.thumbEl.getWidth();
34394                 this.baseScale = width / this.imageEl.OriginWidth;
34395             }
34396
34397             return;
34398         }
34399         
34400         if(this.baseRotate == 6 || this.baseRotate == 8){
34401             
34402             width = this.thumbEl.getHeight();
34403             this.baseScale = width / this.imageEl.OriginHeight;
34404             
34405             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
34406                 height = this.thumbEl.getWidth();
34407                 this.baseScale = height / this.imageEl.OriginHeight;
34408             }
34409             
34410             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34411                 height = this.thumbEl.getWidth();
34412                 this.baseScale = height / this.imageEl.OriginHeight;
34413                 
34414                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
34415                     width = this.thumbEl.getHeight();
34416                     this.baseScale = width / this.imageEl.OriginWidth;
34417                 }
34418             }
34419             
34420             return;
34421         }
34422         
34423         width = this.thumbEl.getWidth();
34424         this.baseScale = width / this.imageEl.OriginWidth;
34425         
34426         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
34427             height = this.thumbEl.getHeight();
34428             this.baseScale = height / this.imageEl.OriginHeight;
34429         }
34430         
34431         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34432             
34433             height = this.thumbEl.getHeight();
34434             this.baseScale = height / this.imageEl.OriginHeight;
34435             
34436             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
34437                 width = this.thumbEl.getWidth();
34438                 this.baseScale = width / this.imageEl.OriginWidth;
34439             }
34440             
34441         }
34442         
34443         return;
34444     },
34445     
34446     getScaleLevel : function()
34447     {
34448         return this.baseScale * Math.pow(1.1, this.scale);
34449     },
34450     
34451     onTouchStart : function(e)
34452     {
34453         if(!this.canvasLoaded){
34454             this.beforeSelectFile(e);
34455             return;
34456         }
34457         
34458         var touches = e.browserEvent.touches;
34459         
34460         if(!touches){
34461             return;
34462         }
34463         
34464         if(touches.length == 1){
34465             this.onMouseDown(e);
34466             return;
34467         }
34468         
34469         if(touches.length != 2){
34470             return;
34471         }
34472         
34473         var coords = [];
34474         
34475         for(var i = 0, finger; finger = touches[i]; i++){
34476             coords.push(finger.pageX, finger.pageY);
34477         }
34478         
34479         var x = Math.pow(coords[0] - coords[2], 2);
34480         var y = Math.pow(coords[1] - coords[3], 2);
34481         
34482         this.startDistance = Math.sqrt(x + y);
34483         
34484         this.startScale = this.scale;
34485         
34486         this.pinching = true;
34487         this.dragable = false;
34488         
34489     },
34490     
34491     onTouchMove : function(e)
34492     {
34493         if(!this.pinching && !this.dragable){
34494             return;
34495         }
34496         
34497         var touches = e.browserEvent.touches;
34498         
34499         if(!touches){
34500             return;
34501         }
34502         
34503         if(this.dragable){
34504             this.onMouseMove(e);
34505             return;
34506         }
34507         
34508         var coords = [];
34509         
34510         for(var i = 0, finger; finger = touches[i]; i++){
34511             coords.push(finger.pageX, finger.pageY);
34512         }
34513         
34514         var x = Math.pow(coords[0] - coords[2], 2);
34515         var y = Math.pow(coords[1] - coords[3], 2);
34516         
34517         this.endDistance = Math.sqrt(x + y);
34518         
34519         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
34520         
34521         if(!this.zoomable()){
34522             this.scale = this.startScale;
34523             return;
34524         }
34525         
34526         this.draw();
34527         
34528     },
34529     
34530     onTouchEnd : function(e)
34531     {
34532         this.pinching = false;
34533         this.dragable = false;
34534         
34535     },
34536     
34537     process : function(file, crop)
34538     {
34539         if(this.loadMask){
34540             this.maskEl.mask(this.loadingText);
34541         }
34542         
34543         this.xhr = new XMLHttpRequest();
34544         
34545         file.xhr = this.xhr;
34546
34547         this.xhr.open(this.method, this.url, true);
34548         
34549         var headers = {
34550             "Accept": "application/json",
34551             "Cache-Control": "no-cache",
34552             "X-Requested-With": "XMLHttpRequest"
34553         };
34554         
34555         for (var headerName in headers) {
34556             var headerValue = headers[headerName];
34557             if (headerValue) {
34558                 this.xhr.setRequestHeader(headerName, headerValue);
34559             }
34560         }
34561         
34562         var _this = this;
34563         
34564         this.xhr.onload = function()
34565         {
34566             _this.xhrOnLoad(_this.xhr);
34567         }
34568         
34569         this.xhr.onerror = function()
34570         {
34571             _this.xhrOnError(_this.xhr);
34572         }
34573         
34574         var formData = new FormData();
34575
34576         formData.append('returnHTML', 'NO');
34577         
34578         if(crop){
34579             formData.append('crop', crop);
34580         }
34581         
34582         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
34583             formData.append(this.paramName, file, file.name);
34584         }
34585         
34586         if(typeof(file.filename) != 'undefined'){
34587             formData.append('filename', file.filename);
34588         }
34589         
34590         if(typeof(file.mimetype) != 'undefined'){
34591             formData.append('mimetype', file.mimetype);
34592         }
34593         
34594         if(this.fireEvent('arrange', this, formData) != false){
34595             this.xhr.send(formData);
34596         };
34597     },
34598     
34599     xhrOnLoad : function(xhr)
34600     {
34601         if(this.loadMask){
34602             this.maskEl.unmask();
34603         }
34604         
34605         if (xhr.readyState !== 4) {
34606             this.fireEvent('exception', this, xhr);
34607             return;
34608         }
34609
34610         var response = Roo.decode(xhr.responseText);
34611         
34612         if(!response.success){
34613             this.fireEvent('exception', this, xhr);
34614             return;
34615         }
34616         
34617         var response = Roo.decode(xhr.responseText);
34618         
34619         this.fireEvent('upload', this, response);
34620         
34621     },
34622     
34623     xhrOnError : function()
34624     {
34625         if(this.loadMask){
34626             this.maskEl.unmask();
34627         }
34628         
34629         Roo.log('xhr on error');
34630         
34631         var response = Roo.decode(xhr.responseText);
34632           
34633         Roo.log(response);
34634         
34635     },
34636     
34637     prepare : function(file)
34638     {   
34639         if(this.loadMask){
34640             this.maskEl.mask(this.loadingText);
34641         }
34642         
34643         this.file = false;
34644         this.exif = {};
34645         
34646         if(typeof(file) === 'string'){
34647             this.loadCanvas(file);
34648             return;
34649         }
34650         
34651         if(!file || !this.urlAPI){
34652             return;
34653         }
34654         
34655         this.file = file;
34656         this.cropType = file.type;
34657         
34658         var _this = this;
34659         
34660         if(this.fireEvent('prepare', this, this.file) != false){
34661             
34662             var reader = new FileReader();
34663             
34664             reader.onload = function (e) {
34665                 if (e.target.error) {
34666                     Roo.log(e.target.error);
34667                     return;
34668                 }
34669                 
34670                 var buffer = e.target.result,
34671                     dataView = new DataView(buffer),
34672                     offset = 2,
34673                     maxOffset = dataView.byteLength - 4,
34674                     markerBytes,
34675                     markerLength;
34676                 
34677                 if (dataView.getUint16(0) === 0xffd8) {
34678                     while (offset < maxOffset) {
34679                         markerBytes = dataView.getUint16(offset);
34680                         
34681                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
34682                             markerLength = dataView.getUint16(offset + 2) + 2;
34683                             if (offset + markerLength > dataView.byteLength) {
34684                                 Roo.log('Invalid meta data: Invalid segment size.');
34685                                 break;
34686                             }
34687                             
34688                             if(markerBytes == 0xffe1){
34689                                 _this.parseExifData(
34690                                     dataView,
34691                                     offset,
34692                                     markerLength
34693                                 );
34694                             }
34695                             
34696                             offset += markerLength;
34697                             
34698                             continue;
34699                         }
34700                         
34701                         break;
34702                     }
34703                     
34704                 }
34705                 
34706                 var url = _this.urlAPI.createObjectURL(_this.file);
34707                 
34708                 _this.loadCanvas(url);
34709                 
34710                 return;
34711             }
34712             
34713             reader.readAsArrayBuffer(this.file);
34714             
34715         }
34716         
34717     },
34718     
34719     parseExifData : function(dataView, offset, length)
34720     {
34721         var tiffOffset = offset + 10,
34722             littleEndian,
34723             dirOffset;
34724     
34725         if (dataView.getUint32(offset + 4) !== 0x45786966) {
34726             // No Exif data, might be XMP data instead
34727             return;
34728         }
34729         
34730         // Check for the ASCII code for "Exif" (0x45786966):
34731         if (dataView.getUint32(offset + 4) !== 0x45786966) {
34732             // No Exif data, might be XMP data instead
34733             return;
34734         }
34735         if (tiffOffset + 8 > dataView.byteLength) {
34736             Roo.log('Invalid Exif data: Invalid segment size.');
34737             return;
34738         }
34739         // Check for the two null bytes:
34740         if (dataView.getUint16(offset + 8) !== 0x0000) {
34741             Roo.log('Invalid Exif data: Missing byte alignment offset.');
34742             return;
34743         }
34744         // Check the byte alignment:
34745         switch (dataView.getUint16(tiffOffset)) {
34746         case 0x4949:
34747             littleEndian = true;
34748             break;
34749         case 0x4D4D:
34750             littleEndian = false;
34751             break;
34752         default:
34753             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
34754             return;
34755         }
34756         // Check for the TIFF tag marker (0x002A):
34757         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
34758             Roo.log('Invalid Exif data: Missing TIFF marker.');
34759             return;
34760         }
34761         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
34762         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
34763         
34764         this.parseExifTags(
34765             dataView,
34766             tiffOffset,
34767             tiffOffset + dirOffset,
34768             littleEndian
34769         );
34770     },
34771     
34772     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
34773     {
34774         var tagsNumber,
34775             dirEndOffset,
34776             i;
34777         if (dirOffset + 6 > dataView.byteLength) {
34778             Roo.log('Invalid Exif data: Invalid directory offset.');
34779             return;
34780         }
34781         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
34782         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
34783         if (dirEndOffset + 4 > dataView.byteLength) {
34784             Roo.log('Invalid Exif data: Invalid directory size.');
34785             return;
34786         }
34787         for (i = 0; i < tagsNumber; i += 1) {
34788             this.parseExifTag(
34789                 dataView,
34790                 tiffOffset,
34791                 dirOffset + 2 + 12 * i, // tag offset
34792                 littleEndian
34793             );
34794         }
34795         // Return the offset to the next directory:
34796         return dataView.getUint32(dirEndOffset, littleEndian);
34797     },
34798     
34799     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
34800     {
34801         var tag = dataView.getUint16(offset, littleEndian);
34802         
34803         this.exif[tag] = this.getExifValue(
34804             dataView,
34805             tiffOffset,
34806             offset,
34807             dataView.getUint16(offset + 2, littleEndian), // tag type
34808             dataView.getUint32(offset + 4, littleEndian), // tag length
34809             littleEndian
34810         );
34811     },
34812     
34813     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
34814     {
34815         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
34816             tagSize,
34817             dataOffset,
34818             values,
34819             i,
34820             str,
34821             c;
34822     
34823         if (!tagType) {
34824             Roo.log('Invalid Exif data: Invalid tag type.');
34825             return;
34826         }
34827         
34828         tagSize = tagType.size * length;
34829         // Determine if the value is contained in the dataOffset bytes,
34830         // or if the value at the dataOffset is a pointer to the actual data:
34831         dataOffset = tagSize > 4 ?
34832                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
34833         if (dataOffset + tagSize > dataView.byteLength) {
34834             Roo.log('Invalid Exif data: Invalid data offset.');
34835             return;
34836         }
34837         if (length === 1) {
34838             return tagType.getValue(dataView, dataOffset, littleEndian);
34839         }
34840         values = [];
34841         for (i = 0; i < length; i += 1) {
34842             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
34843         }
34844         
34845         if (tagType.ascii) {
34846             str = '';
34847             // Concatenate the chars:
34848             for (i = 0; i < values.length; i += 1) {
34849                 c = values[i];
34850                 // Ignore the terminating NULL byte(s):
34851                 if (c === '\u0000') {
34852                     break;
34853                 }
34854                 str += c;
34855             }
34856             return str;
34857         }
34858         return values;
34859     }
34860     
34861 });
34862
34863 Roo.apply(Roo.bootstrap.UploadCropbox, {
34864     tags : {
34865         'Orientation': 0x0112
34866     },
34867     
34868     Orientation: {
34869             1: 0, //'top-left',
34870 //            2: 'top-right',
34871             3: 180, //'bottom-right',
34872 //            4: 'bottom-left',
34873 //            5: 'left-top',
34874             6: 90, //'right-top',
34875 //            7: 'right-bottom',
34876             8: 270 //'left-bottom'
34877     },
34878     
34879     exifTagTypes : {
34880         // byte, 8-bit unsigned int:
34881         1: {
34882             getValue: function (dataView, dataOffset) {
34883                 return dataView.getUint8(dataOffset);
34884             },
34885             size: 1
34886         },
34887         // ascii, 8-bit byte:
34888         2: {
34889             getValue: function (dataView, dataOffset) {
34890                 return String.fromCharCode(dataView.getUint8(dataOffset));
34891             },
34892             size: 1,
34893             ascii: true
34894         },
34895         // short, 16 bit int:
34896         3: {
34897             getValue: function (dataView, dataOffset, littleEndian) {
34898                 return dataView.getUint16(dataOffset, littleEndian);
34899             },
34900             size: 2
34901         },
34902         // long, 32 bit int:
34903         4: {
34904             getValue: function (dataView, dataOffset, littleEndian) {
34905                 return dataView.getUint32(dataOffset, littleEndian);
34906             },
34907             size: 4
34908         },
34909         // rational = two long values, first is numerator, second is denominator:
34910         5: {
34911             getValue: function (dataView, dataOffset, littleEndian) {
34912                 return dataView.getUint32(dataOffset, littleEndian) /
34913                     dataView.getUint32(dataOffset + 4, littleEndian);
34914             },
34915             size: 8
34916         },
34917         // slong, 32 bit signed int:
34918         9: {
34919             getValue: function (dataView, dataOffset, littleEndian) {
34920                 return dataView.getInt32(dataOffset, littleEndian);
34921             },
34922             size: 4
34923         },
34924         // srational, two slongs, first is numerator, second is denominator:
34925         10: {
34926             getValue: function (dataView, dataOffset, littleEndian) {
34927                 return dataView.getInt32(dataOffset, littleEndian) /
34928                     dataView.getInt32(dataOffset + 4, littleEndian);
34929             },
34930             size: 8
34931         }
34932     },
34933     
34934     footer : {
34935         STANDARD : [
34936             {
34937                 tag : 'div',
34938                 cls : 'btn-group roo-upload-cropbox-rotate-left',
34939                 action : 'rotate-left',
34940                 cn : [
34941                     {
34942                         tag : 'button',
34943                         cls : 'btn btn-default',
34944                         html : '<i class="fa fa-undo"></i>'
34945                     }
34946                 ]
34947             },
34948             {
34949                 tag : 'div',
34950                 cls : 'btn-group roo-upload-cropbox-picture',
34951                 action : 'picture',
34952                 cn : [
34953                     {
34954                         tag : 'button',
34955                         cls : 'btn btn-default',
34956                         html : '<i class="fa fa-picture-o"></i>'
34957                     }
34958                 ]
34959             },
34960             {
34961                 tag : 'div',
34962                 cls : 'btn-group roo-upload-cropbox-rotate-right',
34963                 action : 'rotate-right',
34964                 cn : [
34965                     {
34966                         tag : 'button',
34967                         cls : 'btn btn-default',
34968                         html : '<i class="fa fa-repeat"></i>'
34969                     }
34970                 ]
34971             }
34972         ],
34973         DOCUMENT : [
34974             {
34975                 tag : 'div',
34976                 cls : 'btn-group roo-upload-cropbox-rotate-left',
34977                 action : 'rotate-left',
34978                 cn : [
34979                     {
34980                         tag : 'button',
34981                         cls : 'btn btn-default',
34982                         html : '<i class="fa fa-undo"></i>'
34983                     }
34984                 ]
34985             },
34986             {
34987                 tag : 'div',
34988                 cls : 'btn-group roo-upload-cropbox-download',
34989                 action : 'download',
34990                 cn : [
34991                     {
34992                         tag : 'button',
34993                         cls : 'btn btn-default',
34994                         html : '<i class="fa fa-download"></i>'
34995                     }
34996                 ]
34997             },
34998             {
34999                 tag : 'div',
35000                 cls : 'btn-group roo-upload-cropbox-crop',
35001                 action : 'crop',
35002                 cn : [
35003                     {
35004                         tag : 'button',
35005                         cls : 'btn btn-default',
35006                         html : '<i class="fa fa-crop"></i>'
35007                     }
35008                 ]
35009             },
35010             {
35011                 tag : 'div',
35012                 cls : 'btn-group roo-upload-cropbox-trash',
35013                 action : 'trash',
35014                 cn : [
35015                     {
35016                         tag : 'button',
35017                         cls : 'btn btn-default',
35018                         html : '<i class="fa fa-trash"></i>'
35019                     }
35020                 ]
35021             },
35022             {
35023                 tag : 'div',
35024                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35025                 action : 'rotate-right',
35026                 cn : [
35027                     {
35028                         tag : 'button',
35029                         cls : 'btn btn-default',
35030                         html : '<i class="fa fa-repeat"></i>'
35031                     }
35032                 ]
35033             }
35034         ],
35035         ROTATOR : [
35036             {
35037                 tag : 'div',
35038                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35039                 action : 'rotate-left',
35040                 cn : [
35041                     {
35042                         tag : 'button',
35043                         cls : 'btn btn-default',
35044                         html : '<i class="fa fa-undo"></i>'
35045                     }
35046                 ]
35047             },
35048             {
35049                 tag : 'div',
35050                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35051                 action : 'rotate-right',
35052                 cn : [
35053                     {
35054                         tag : 'button',
35055                         cls : 'btn btn-default',
35056                         html : '<i class="fa fa-repeat"></i>'
35057                     }
35058                 ]
35059             }
35060         ]
35061     }
35062 });
35063
35064 /*
35065 * Licence: LGPL
35066 */
35067
35068 /**
35069  * @class Roo.bootstrap.DocumentManager
35070  * @extends Roo.bootstrap.Component
35071  * Bootstrap DocumentManager class
35072  * @cfg {String} paramName default 'imageUpload'
35073  * @cfg {String} toolTipName default 'filename'
35074  * @cfg {String} method default POST
35075  * @cfg {String} url action url
35076  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
35077  * @cfg {Boolean} multiple multiple upload default true
35078  * @cfg {Number} thumbSize default 300
35079  * @cfg {String} fieldLabel
35080  * @cfg {Number} labelWidth default 4
35081  * @cfg {String} labelAlign (left|top) default left
35082  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
35083 * @cfg {Number} labellg set the width of label (1-12)
35084  * @cfg {Number} labelmd set the width of label (1-12)
35085  * @cfg {Number} labelsm set the width of label (1-12)
35086  * @cfg {Number} labelxs set the width of label (1-12)
35087  * 
35088  * @constructor
35089  * Create a new DocumentManager
35090  * @param {Object} config The config object
35091  */
35092
35093 Roo.bootstrap.DocumentManager = function(config){
35094     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
35095     
35096     this.files = [];
35097     this.delegates = [];
35098     
35099     this.addEvents({
35100         /**
35101          * @event initial
35102          * Fire when initial the DocumentManager
35103          * @param {Roo.bootstrap.DocumentManager} this
35104          */
35105         "initial" : true,
35106         /**
35107          * @event inspect
35108          * inspect selected file
35109          * @param {Roo.bootstrap.DocumentManager} this
35110          * @param {File} file
35111          */
35112         "inspect" : true,
35113         /**
35114          * @event exception
35115          * Fire when xhr load exception
35116          * @param {Roo.bootstrap.DocumentManager} this
35117          * @param {XMLHttpRequest} xhr
35118          */
35119         "exception" : true,
35120         /**
35121          * @event afterupload
35122          * Fire when xhr load exception
35123          * @param {Roo.bootstrap.DocumentManager} this
35124          * @param {XMLHttpRequest} xhr
35125          */
35126         "afterupload" : true,
35127         /**
35128          * @event prepare
35129          * prepare the form data
35130          * @param {Roo.bootstrap.DocumentManager} this
35131          * @param {Object} formData
35132          */
35133         "prepare" : true,
35134         /**
35135          * @event remove
35136          * Fire when remove the file
35137          * @param {Roo.bootstrap.DocumentManager} this
35138          * @param {Object} file
35139          */
35140         "remove" : true,
35141         /**
35142          * @event refresh
35143          * Fire after refresh the file
35144          * @param {Roo.bootstrap.DocumentManager} this
35145          */
35146         "refresh" : true,
35147         /**
35148          * @event click
35149          * Fire after click the image
35150          * @param {Roo.bootstrap.DocumentManager} this
35151          * @param {Object} file
35152          */
35153         "click" : true,
35154         /**
35155          * @event edit
35156          * Fire when upload a image and editable set to true
35157          * @param {Roo.bootstrap.DocumentManager} this
35158          * @param {Object} file
35159          */
35160         "edit" : true,
35161         /**
35162          * @event beforeselectfile
35163          * Fire before select file
35164          * @param {Roo.bootstrap.DocumentManager} this
35165          */
35166         "beforeselectfile" : true,
35167         /**
35168          * @event process
35169          * Fire before process file
35170          * @param {Roo.bootstrap.DocumentManager} this
35171          * @param {Object} file
35172          */
35173         "process" : true,
35174         /**
35175          * @event previewrendered
35176          * Fire when preview rendered
35177          * @param {Roo.bootstrap.DocumentManager} this
35178          * @param {Object} file
35179          */
35180         "previewrendered" : true,
35181         /**
35182          */
35183         "previewResize" : true
35184         
35185     });
35186 };
35187
35188 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
35189     
35190     boxes : 0,
35191     inputName : '',
35192     thumbSize : 300,
35193     multiple : true,
35194     files : false,
35195     method : 'POST',
35196     url : '',
35197     paramName : 'imageUpload',
35198     toolTipName : 'filename',
35199     fieldLabel : '',
35200     labelWidth : 4,
35201     labelAlign : 'left',
35202     editable : true,
35203     delegates : false,
35204     xhr : false, 
35205     
35206     labellg : 0,
35207     labelmd : 0,
35208     labelsm : 0,
35209     labelxs : 0,
35210     
35211     getAutoCreate : function()
35212     {   
35213         var managerWidget = {
35214             tag : 'div',
35215             cls : 'roo-document-manager',
35216             cn : [
35217                 {
35218                     tag : 'input',
35219                     cls : 'roo-document-manager-selector',
35220                     type : 'file'
35221                 },
35222                 {
35223                     tag : 'div',
35224                     cls : 'roo-document-manager-uploader',
35225                     cn : [
35226                         {
35227                             tag : 'div',
35228                             cls : 'roo-document-manager-upload-btn',
35229                             html : '<i class="fa fa-plus"></i>'
35230                         }
35231                     ]
35232                     
35233                 }
35234             ]
35235         };
35236         
35237         var content = [
35238             {
35239                 tag : 'div',
35240                 cls : 'column col-md-12',
35241                 cn : managerWidget
35242             }
35243         ];
35244         
35245         if(this.fieldLabel.length){
35246             
35247             content = [
35248                 {
35249                     tag : 'div',
35250                     cls : 'column col-md-12',
35251                     html : this.fieldLabel
35252                 },
35253                 {
35254                     tag : 'div',
35255                     cls : 'column col-md-12',
35256                     cn : managerWidget
35257                 }
35258             ];
35259
35260             if(this.labelAlign == 'left'){
35261                 content = [
35262                     {
35263                         tag : 'div',
35264                         cls : 'column',
35265                         html : this.fieldLabel
35266                     },
35267                     {
35268                         tag : 'div',
35269                         cls : 'column',
35270                         cn : managerWidget
35271                     }
35272                 ];
35273                 
35274                 if(this.labelWidth > 12){
35275                     content[0].style = "width: " + this.labelWidth + 'px';
35276                 }
35277
35278                 if(this.labelWidth < 13 && this.labelmd == 0){
35279                     this.labelmd = this.labelWidth;
35280                 }
35281
35282                 if(this.labellg > 0){
35283                     content[0].cls += ' col-lg-' + this.labellg;
35284                     content[1].cls += ' col-lg-' + (12 - this.labellg);
35285                 }
35286
35287                 if(this.labelmd > 0){
35288                     content[0].cls += ' col-md-' + this.labelmd;
35289                     content[1].cls += ' col-md-' + (12 - this.labelmd);
35290                 }
35291
35292                 if(this.labelsm > 0){
35293                     content[0].cls += ' col-sm-' + this.labelsm;
35294                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
35295                 }
35296
35297                 if(this.labelxs > 0){
35298                     content[0].cls += ' col-xs-' + this.labelxs;
35299                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
35300                 }
35301                 
35302             }
35303         }
35304         
35305         var cfg = {
35306             tag : 'div',
35307             cls : 'row clearfix',
35308             cn : content
35309         };
35310         
35311         return cfg;
35312         
35313     },
35314     
35315     initEvents : function()
35316     {
35317         this.managerEl = this.el.select('.roo-document-manager', true).first();
35318         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35319         
35320         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
35321         this.selectorEl.hide();
35322         
35323         if(this.multiple){
35324             this.selectorEl.attr('multiple', 'multiple');
35325         }
35326         
35327         this.selectorEl.on('change', this.onFileSelected, this);
35328         
35329         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
35330         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35331         
35332         this.uploader.on('click', this.onUploaderClick, this);
35333         
35334         this.renderProgressDialog();
35335         
35336         var _this = this;
35337         
35338         window.addEventListener("resize", function() { _this.refresh(); } );
35339         
35340         this.fireEvent('initial', this);
35341     },
35342     
35343     renderProgressDialog : function()
35344     {
35345         var _this = this;
35346         
35347         this.progressDialog = new Roo.bootstrap.Modal({
35348             cls : 'roo-document-manager-progress-dialog',
35349             allow_close : false,
35350             animate : false,
35351             title : '',
35352             buttons : [
35353                 {
35354                     name  :'cancel',
35355                     weight : 'danger',
35356                     html : 'Cancel'
35357                 }
35358             ], 
35359             listeners : { 
35360                 btnclick : function() {
35361                     _this.uploadCancel();
35362                     this.hide();
35363                 }
35364             }
35365         });
35366          
35367         this.progressDialog.render(Roo.get(document.body));
35368          
35369         this.progress = new Roo.bootstrap.Progress({
35370             cls : 'roo-document-manager-progress',
35371             active : true,
35372             striped : true
35373         });
35374         
35375         this.progress.render(this.progressDialog.getChildContainer());
35376         
35377         this.progressBar = new Roo.bootstrap.ProgressBar({
35378             cls : 'roo-document-manager-progress-bar',
35379             aria_valuenow : 0,
35380             aria_valuemin : 0,
35381             aria_valuemax : 12,
35382             panel : 'success'
35383         });
35384         
35385         this.progressBar.render(this.progress.getChildContainer());
35386     },
35387     
35388     onUploaderClick : function(e)
35389     {
35390         e.preventDefault();
35391      
35392         if(this.fireEvent('beforeselectfile', this) != false){
35393             this.selectorEl.dom.click();
35394         }
35395         
35396     },
35397     
35398     onFileSelected : function(e)
35399     {
35400         e.preventDefault();
35401         
35402         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35403             return;
35404         }
35405         
35406         Roo.each(this.selectorEl.dom.files, function(file){
35407             if(this.fireEvent('inspect', this, file) != false){
35408                 this.files.push(file);
35409             }
35410         }, this);
35411         
35412         this.queue();
35413         
35414     },
35415     
35416     queue : function()
35417     {
35418         this.selectorEl.dom.value = '';
35419         
35420         if(!this.files || !this.files.length){
35421             return;
35422         }
35423         
35424         if(this.boxes > 0 && this.files.length > this.boxes){
35425             this.files = this.files.slice(0, this.boxes);
35426         }
35427         
35428         this.uploader.show();
35429         
35430         if(this.boxes > 0 && this.files.length > this.boxes - 1){
35431             this.uploader.hide();
35432         }
35433         
35434         var _this = this;
35435         
35436         var files = [];
35437         
35438         var docs = [];
35439         
35440         Roo.each(this.files, function(file){
35441             
35442             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35443                 var f = this.renderPreview(file);
35444                 files.push(f);
35445                 return;
35446             }
35447             
35448             if(file.type.indexOf('image') != -1){
35449                 this.delegates.push(
35450                     (function(){
35451                         _this.process(file);
35452                     }).createDelegate(this)
35453                 );
35454         
35455                 return;
35456             }
35457             
35458             docs.push(
35459                 (function(){
35460                     _this.process(file);
35461                 }).createDelegate(this)
35462             );
35463             
35464         }, this);
35465         
35466         this.files = files;
35467         
35468         this.delegates = this.delegates.concat(docs);
35469         
35470         if(!this.delegates.length){
35471             this.refresh();
35472             return;
35473         }
35474         
35475         this.progressBar.aria_valuemax = this.delegates.length;
35476         
35477         this.arrange();
35478         
35479         return;
35480     },
35481     
35482     arrange : function()
35483     {
35484         if(!this.delegates.length){
35485             this.progressDialog.hide();
35486             this.refresh();
35487             return;
35488         }
35489         
35490         var delegate = this.delegates.shift();
35491         
35492         this.progressDialog.show();
35493         
35494         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
35495         
35496         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
35497         
35498         delegate();
35499     },
35500     
35501     refresh : function()
35502     {
35503         this.uploader.show();
35504         
35505         if(this.boxes > 0 && this.files.length > this.boxes - 1){
35506             this.uploader.hide();
35507         }
35508         
35509         Roo.isTouch ? this.closable(false) : this.closable(true);
35510         
35511         this.fireEvent('refresh', this);
35512     },
35513     
35514     onRemove : function(e, el, o)
35515     {
35516         e.preventDefault();
35517         
35518         this.fireEvent('remove', this, o);
35519         
35520     },
35521     
35522     remove : function(o)
35523     {
35524         var files = [];
35525         
35526         Roo.each(this.files, function(file){
35527             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
35528                 files.push(file);
35529                 return;
35530             }
35531
35532             o.target.remove();
35533
35534         }, this);
35535         
35536         this.files = files;
35537         
35538         this.refresh();
35539     },
35540     
35541     clear : function()
35542     {
35543         Roo.each(this.files, function(file){
35544             if(!file.target){
35545                 return;
35546             }
35547             
35548             file.target.remove();
35549
35550         }, this);
35551         
35552         this.files = [];
35553         
35554         this.refresh();
35555     },
35556     
35557     onClick : function(e, el, o)
35558     {
35559         e.preventDefault();
35560         
35561         this.fireEvent('click', this, o);
35562         
35563     },
35564     
35565     closable : function(closable)
35566     {
35567         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
35568             
35569             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35570             
35571             if(closable){
35572                 el.show();
35573                 return;
35574             }
35575             
35576             el.hide();
35577             
35578         }, this);
35579     },
35580     
35581     xhrOnLoad : function(xhr)
35582     {
35583         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35584             el.remove();
35585         }, this);
35586         
35587         if (xhr.readyState !== 4) {
35588             this.arrange();
35589             this.fireEvent('exception', this, xhr);
35590             return;
35591         }
35592
35593         var response = Roo.decode(xhr.responseText);
35594         
35595         if(!response.success){
35596             this.arrange();
35597             this.fireEvent('exception', this, xhr);
35598             return;
35599         }
35600         
35601         var file = this.renderPreview(response.data);
35602         
35603         this.files.push(file);
35604         
35605         this.arrange();
35606         
35607         this.fireEvent('afterupload', this, xhr);
35608         
35609     },
35610     
35611     xhrOnError : function(xhr)
35612     {
35613         Roo.log('xhr on error');
35614         
35615         var response = Roo.decode(xhr.responseText);
35616           
35617         Roo.log(response);
35618         
35619         this.arrange();
35620     },
35621     
35622     process : function(file)
35623     {
35624         if(this.fireEvent('process', this, file) !== false){
35625             if(this.editable && file.type.indexOf('image') != -1){
35626                 this.fireEvent('edit', this, file);
35627                 return;
35628             }
35629
35630             this.uploadStart(file, false);
35631
35632             return;
35633         }
35634         
35635     },
35636     
35637     uploadStart : function(file, crop)
35638     {
35639         this.xhr = new XMLHttpRequest();
35640         
35641         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35642             this.arrange();
35643             return;
35644         }
35645         
35646         file.xhr = this.xhr;
35647             
35648         this.managerEl.createChild({
35649             tag : 'div',
35650             cls : 'roo-document-manager-loading',
35651             cn : [
35652                 {
35653                     tag : 'div',
35654                     tooltip : file.name,
35655                     cls : 'roo-document-manager-thumb',
35656                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
35657                 }
35658             ]
35659
35660         });
35661
35662         this.xhr.open(this.method, this.url, true);
35663         
35664         var headers = {
35665             "Accept": "application/json",
35666             "Cache-Control": "no-cache",
35667             "X-Requested-With": "XMLHttpRequest"
35668         };
35669         
35670         for (var headerName in headers) {
35671             var headerValue = headers[headerName];
35672             if (headerValue) {
35673                 this.xhr.setRequestHeader(headerName, headerValue);
35674             }
35675         }
35676         
35677         var _this = this;
35678         
35679         this.xhr.onload = function()
35680         {
35681             _this.xhrOnLoad(_this.xhr);
35682         }
35683         
35684         this.xhr.onerror = function()
35685         {
35686             _this.xhrOnError(_this.xhr);
35687         }
35688         
35689         var formData = new FormData();
35690
35691         formData.append('returnHTML', 'NO');
35692         
35693         if(crop){
35694             formData.append('crop', crop);
35695         }
35696         
35697         formData.append(this.paramName, file, file.name);
35698         
35699         var options = {
35700             file : file, 
35701             manually : false
35702         };
35703         
35704         if(this.fireEvent('prepare', this, formData, options) != false){
35705             
35706             if(options.manually){
35707                 return;
35708             }
35709             
35710             this.xhr.send(formData);
35711             return;
35712         };
35713         
35714         this.uploadCancel();
35715     },
35716     
35717     uploadCancel : function()
35718     {
35719         if (this.xhr) {
35720             this.xhr.abort();
35721         }
35722         
35723         this.delegates = [];
35724         
35725         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35726             el.remove();
35727         }, this);
35728         
35729         this.arrange();
35730     },
35731     
35732     renderPreview : function(file)
35733     {
35734         if(typeof(file.target) != 'undefined' && file.target){
35735             return file;
35736         }
35737         
35738         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
35739         
35740         var previewEl = this.managerEl.createChild({
35741             tag : 'div',
35742             cls : 'roo-document-manager-preview',
35743             cn : [
35744                 {
35745                     tag : 'div',
35746                     tooltip : file[this.toolTipName],
35747                     cls : 'roo-document-manager-thumb',
35748                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
35749                 },
35750                 {
35751                     tag : 'button',
35752                     cls : 'close',
35753                     html : '<i class="fa fa-times-circle"></i>'
35754                 }
35755             ]
35756         });
35757
35758         var close = previewEl.select('button.close', true).first();
35759
35760         close.on('click', this.onRemove, this, file);
35761
35762         file.target = previewEl;
35763
35764         var image = previewEl.select('img', true).first();
35765         
35766         var _this = this;
35767         
35768         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
35769         
35770         image.on('click', this.onClick, this, file);
35771         
35772         this.fireEvent('previewrendered', this, file);
35773         
35774         return file;
35775         
35776     },
35777     
35778     onPreviewLoad : function(file, image)
35779     {
35780         if(typeof(file.target) == 'undefined' || !file.target){
35781             return;
35782         }
35783         
35784         var width = image.dom.naturalWidth || image.dom.width;
35785         var height = image.dom.naturalHeight || image.dom.height;
35786         
35787         if(!this.previewResize) {
35788             return;
35789         }
35790         
35791         if(width > height){
35792             file.target.addClass('wide');
35793             return;
35794         }
35795         
35796         file.target.addClass('tall');
35797         return;
35798         
35799     },
35800     
35801     uploadFromSource : function(file, crop)
35802     {
35803         this.xhr = new XMLHttpRequest();
35804         
35805         this.managerEl.createChild({
35806             tag : 'div',
35807             cls : 'roo-document-manager-loading',
35808             cn : [
35809                 {
35810                     tag : 'div',
35811                     tooltip : file.name,
35812                     cls : 'roo-document-manager-thumb',
35813                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
35814                 }
35815             ]
35816
35817         });
35818
35819         this.xhr.open(this.method, this.url, true);
35820         
35821         var headers = {
35822             "Accept": "application/json",
35823             "Cache-Control": "no-cache",
35824             "X-Requested-With": "XMLHttpRequest"
35825         };
35826         
35827         for (var headerName in headers) {
35828             var headerValue = headers[headerName];
35829             if (headerValue) {
35830                 this.xhr.setRequestHeader(headerName, headerValue);
35831             }
35832         }
35833         
35834         var _this = this;
35835         
35836         this.xhr.onload = function()
35837         {
35838             _this.xhrOnLoad(_this.xhr);
35839         }
35840         
35841         this.xhr.onerror = function()
35842         {
35843             _this.xhrOnError(_this.xhr);
35844         }
35845         
35846         var formData = new FormData();
35847
35848         formData.append('returnHTML', 'NO');
35849         
35850         formData.append('crop', crop);
35851         
35852         if(typeof(file.filename) != 'undefined'){
35853             formData.append('filename', file.filename);
35854         }
35855         
35856         if(typeof(file.mimetype) != 'undefined'){
35857             formData.append('mimetype', file.mimetype);
35858         }
35859         
35860         Roo.log(formData);
35861         
35862         if(this.fireEvent('prepare', this, formData) != false){
35863             this.xhr.send(formData);
35864         };
35865     }
35866 });
35867
35868 /*
35869 * Licence: LGPL
35870 */
35871
35872 /**
35873  * @class Roo.bootstrap.DocumentViewer
35874  * @extends Roo.bootstrap.Component
35875  * Bootstrap DocumentViewer class
35876  * @cfg {Boolean} showDownload (true|false) show download button (default true)
35877  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
35878  * 
35879  * @constructor
35880  * Create a new DocumentViewer
35881  * @param {Object} config The config object
35882  */
35883
35884 Roo.bootstrap.DocumentViewer = function(config){
35885     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
35886     
35887     this.addEvents({
35888         /**
35889          * @event initial
35890          * Fire after initEvent
35891          * @param {Roo.bootstrap.DocumentViewer} this
35892          */
35893         "initial" : true,
35894         /**
35895          * @event click
35896          * Fire after click
35897          * @param {Roo.bootstrap.DocumentViewer} this
35898          */
35899         "click" : true,
35900         /**
35901          * @event download
35902          * Fire after download button
35903          * @param {Roo.bootstrap.DocumentViewer} this
35904          */
35905         "download" : true,
35906         /**
35907          * @event trash
35908          * Fire after trash button
35909          * @param {Roo.bootstrap.DocumentViewer} this
35910          */
35911         "trash" : true
35912         
35913     });
35914 };
35915
35916 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
35917     
35918     showDownload : true,
35919     
35920     showTrash : true,
35921     
35922     getAutoCreate : function()
35923     {
35924         var cfg = {
35925             tag : 'div',
35926             cls : 'roo-document-viewer',
35927             cn : [
35928                 {
35929                     tag : 'div',
35930                     cls : 'roo-document-viewer-body',
35931                     cn : [
35932                         {
35933                             tag : 'div',
35934                             cls : 'roo-document-viewer-thumb',
35935                             cn : [
35936                                 {
35937                                     tag : 'img',
35938                                     cls : 'roo-document-viewer-image'
35939                                 }
35940                             ]
35941                         }
35942                     ]
35943                 },
35944                 {
35945                     tag : 'div',
35946                     cls : 'roo-document-viewer-footer',
35947                     cn : {
35948                         tag : 'div',
35949                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
35950                         cn : [
35951                             {
35952                                 tag : 'div',
35953                                 cls : 'btn-group roo-document-viewer-download',
35954                                 cn : [
35955                                     {
35956                                         tag : 'button',
35957                                         cls : 'btn btn-default',
35958                                         html : '<i class="fa fa-download"></i>'
35959                                     }
35960                                 ]
35961                             },
35962                             {
35963                                 tag : 'div',
35964                                 cls : 'btn-group roo-document-viewer-trash',
35965                                 cn : [
35966                                     {
35967                                         tag : 'button',
35968                                         cls : 'btn btn-default',
35969                                         html : '<i class="fa fa-trash"></i>'
35970                                     }
35971                                 ]
35972                             }
35973                         ]
35974                     }
35975                 }
35976             ]
35977         };
35978         
35979         return cfg;
35980     },
35981     
35982     initEvents : function()
35983     {
35984         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
35985         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35986         
35987         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
35988         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35989         
35990         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
35991         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35992         
35993         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
35994         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
35995         
35996         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
35997         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
35998         
35999         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
36000         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
36001         
36002         this.bodyEl.on('click', this.onClick, this);
36003         this.downloadBtn.on('click', this.onDownload, this);
36004         this.trashBtn.on('click', this.onTrash, this);
36005         
36006         this.downloadBtn.hide();
36007         this.trashBtn.hide();
36008         
36009         if(this.showDownload){
36010             this.downloadBtn.show();
36011         }
36012         
36013         if(this.showTrash){
36014             this.trashBtn.show();
36015         }
36016         
36017         if(!this.showDownload && !this.showTrash) {
36018             this.footerEl.hide();
36019         }
36020         
36021     },
36022     
36023     initial : function()
36024     {
36025         this.fireEvent('initial', this);
36026         
36027     },
36028     
36029     onClick : function(e)
36030     {
36031         e.preventDefault();
36032         
36033         this.fireEvent('click', this);
36034     },
36035     
36036     onDownload : function(e)
36037     {
36038         e.preventDefault();
36039         
36040         this.fireEvent('download', this);
36041     },
36042     
36043     onTrash : function(e)
36044     {
36045         e.preventDefault();
36046         
36047         this.fireEvent('trash', this);
36048     }
36049     
36050 });
36051 /*
36052  * - LGPL
36053  *
36054  * FieldLabel
36055  * 
36056  */
36057
36058 /**
36059  * @class Roo.bootstrap.form.FieldLabel
36060  * @extends Roo.bootstrap.Component
36061  * Bootstrap FieldLabel class
36062  * @cfg {String} html contents of the element
36063  * @cfg {String} tag tag of the element default label
36064  * @cfg {String} cls class of the element
36065  * @cfg {String} target label target 
36066  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
36067  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
36068  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
36069  * @cfg {String} iconTooltip default "This field is required"
36070  * @cfg {String} indicatorpos (left|right) default left
36071  * 
36072  * @constructor
36073  * Create a new FieldLabel
36074  * @param {Object} config The config object
36075  */
36076
36077 Roo.bootstrap.form.FieldLabel = function(config){
36078     Roo.bootstrap.Element.superclass.constructor.call(this, config);
36079     
36080     this.addEvents({
36081             /**
36082              * @event invalid
36083              * Fires after the field has been marked as invalid.
36084              * @param {Roo.form.FieldLabel} this
36085              * @param {String} msg The validation message
36086              */
36087             invalid : true,
36088             /**
36089              * @event valid
36090              * Fires after the field has been validated with no errors.
36091              * @param {Roo.form.FieldLabel} this
36092              */
36093             valid : true
36094         });
36095 };
36096
36097 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
36098     
36099     tag: 'label',
36100     cls: '',
36101     html: '',
36102     target: '',
36103     allowBlank : true,
36104     invalidClass : 'has-warning',
36105     validClass : 'has-success',
36106     iconTooltip : 'This field is required',
36107     indicatorpos : 'left',
36108     
36109     getAutoCreate : function(){
36110         
36111         var cls = "";
36112         if (!this.allowBlank) {
36113             cls  = "visible";
36114         }
36115         
36116         var cfg = {
36117             tag : this.tag,
36118             cls : 'roo-bootstrap-field-label ' + this.cls,
36119             for : this.target,
36120             cn : [
36121                 {
36122                     tag : 'i',
36123                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
36124                     tooltip : this.iconTooltip
36125                 },
36126                 {
36127                     tag : 'span',
36128                     html : this.html
36129                 }
36130             ] 
36131         };
36132         
36133         if(this.indicatorpos == 'right'){
36134             var cfg = {
36135                 tag : this.tag,
36136                 cls : 'roo-bootstrap-field-label ' + this.cls,
36137                 for : this.target,
36138                 cn : [
36139                     {
36140                         tag : 'span',
36141                         html : this.html
36142                     },
36143                     {
36144                         tag : 'i',
36145                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
36146                         tooltip : this.iconTooltip
36147                     }
36148                 ] 
36149             };
36150         }
36151         
36152         return cfg;
36153     },
36154     
36155     initEvents: function() 
36156     {
36157         Roo.bootstrap.Element.superclass.initEvents.call(this);
36158         
36159         this.indicator = this.indicatorEl();
36160         
36161         if(this.indicator){
36162             this.indicator.removeClass('visible');
36163             this.indicator.addClass('invisible');
36164         }
36165         
36166         Roo.bootstrap.form.FieldLabel.register(this);
36167     },
36168     
36169     indicatorEl : function()
36170     {
36171         var indicator = this.el.select('i.roo-required-indicator',true).first();
36172         
36173         if(!indicator){
36174             return false;
36175         }
36176         
36177         return indicator;
36178         
36179     },
36180     
36181     /**
36182      * Mark this field as valid
36183      */
36184     markValid : function()
36185     {
36186         if(this.indicator){
36187             this.indicator.removeClass('visible');
36188             this.indicator.addClass('invisible');
36189         }
36190         if (Roo.bootstrap.version == 3) {
36191             this.el.removeClass(this.invalidClass);
36192             this.el.addClass(this.validClass);
36193         } else {
36194             this.el.removeClass('is-invalid');
36195             this.el.addClass('is-valid');
36196         }
36197         
36198         
36199         this.fireEvent('valid', this);
36200     },
36201     
36202     /**
36203      * Mark this field as invalid
36204      * @param {String} msg The validation message
36205      */
36206     markInvalid : function(msg)
36207     {
36208         if(this.indicator){
36209             this.indicator.removeClass('invisible');
36210             this.indicator.addClass('visible');
36211         }
36212           if (Roo.bootstrap.version == 3) {
36213             this.el.removeClass(this.validClass);
36214             this.el.addClass(this.invalidClass);
36215         } else {
36216             this.el.removeClass('is-valid');
36217             this.el.addClass('is-invalid');
36218         }
36219         
36220         
36221         this.fireEvent('invalid', this, msg);
36222     }
36223     
36224    
36225 });
36226
36227 Roo.apply(Roo.bootstrap.form.FieldLabel, {
36228     
36229     groups: {},
36230     
36231      /**
36232     * register a FieldLabel Group
36233     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
36234     */
36235     register : function(label)
36236     {
36237         if(this.groups.hasOwnProperty(label.target)){
36238             return;
36239         }
36240      
36241         this.groups[label.target] = label;
36242         
36243     },
36244     /**
36245     * fetch a FieldLabel Group based on the target
36246     * @param {string} target
36247     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
36248     */
36249     get: function(target) {
36250         if (typeof(this.groups[target]) == 'undefined') {
36251             return false;
36252         }
36253         
36254         return this.groups[target] ;
36255     }
36256 });
36257
36258  
36259
36260  /*
36261  * - LGPL
36262  *
36263  * page DateSplitField.
36264  * 
36265  */
36266
36267
36268 /**
36269  * @class Roo.bootstrap.form.DateSplitField
36270  * @extends Roo.bootstrap.Component
36271  * Bootstrap DateSplitField class
36272  * @cfg {string} fieldLabel - the label associated
36273  * @cfg {Number} labelWidth set the width of label (0-12)
36274  * @cfg {String} labelAlign (top|left)
36275  * @cfg {Boolean} dayAllowBlank (true|false) default false
36276  * @cfg {Boolean} monthAllowBlank (true|false) default false
36277  * @cfg {Boolean} yearAllowBlank (true|false) default false
36278  * @cfg {string} dayPlaceholder 
36279  * @cfg {string} monthPlaceholder
36280  * @cfg {string} yearPlaceholder
36281  * @cfg {string} dayFormat default 'd'
36282  * @cfg {string} monthFormat default 'm'
36283  * @cfg {string} yearFormat default 'Y'
36284  * @cfg {Number} labellg set the width of label (1-12)
36285  * @cfg {Number} labelmd set the width of label (1-12)
36286  * @cfg {Number} labelsm set the width of label (1-12)
36287  * @cfg {Number} labelxs set the width of label (1-12)
36288
36289  *     
36290  * @constructor
36291  * Create a new DateSplitField
36292  * @param {Object} config The config object
36293  */
36294
36295 Roo.bootstrap.form.DateSplitField = function(config){
36296     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
36297     
36298     this.addEvents({
36299         // raw events
36300          /**
36301          * @event years
36302          * getting the data of years
36303          * @param {Roo.bootstrap.form.DateSplitField} this
36304          * @param {Object} years
36305          */
36306         "years" : true,
36307         /**
36308          * @event days
36309          * getting the data of days
36310          * @param {Roo.bootstrap.form.DateSplitField} this
36311          * @param {Object} days
36312          */
36313         "days" : true,
36314         /**
36315          * @event invalid
36316          * Fires after the field has been marked as invalid.
36317          * @param {Roo.form.Field} this
36318          * @param {String} msg The validation message
36319          */
36320         invalid : true,
36321        /**
36322          * @event valid
36323          * Fires after the field has been validated with no errors.
36324          * @param {Roo.form.Field} this
36325          */
36326         valid : true
36327     });
36328 };
36329
36330 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
36331     
36332     fieldLabel : '',
36333     labelAlign : 'top',
36334     labelWidth : 3,
36335     dayAllowBlank : false,
36336     monthAllowBlank : false,
36337     yearAllowBlank : false,
36338     dayPlaceholder : '',
36339     monthPlaceholder : '',
36340     yearPlaceholder : '',
36341     dayFormat : 'd',
36342     monthFormat : 'm',
36343     yearFormat : 'Y',
36344     isFormField : true,
36345     labellg : 0,
36346     labelmd : 0,
36347     labelsm : 0,
36348     labelxs : 0,
36349     
36350     getAutoCreate : function()
36351     {
36352         var cfg = {
36353             tag : 'div',
36354             cls : 'row roo-date-split-field-group',
36355             cn : [
36356                 {
36357                     tag : 'input',
36358                     type : 'hidden',
36359                     cls : 'form-hidden-field roo-date-split-field-group-value',
36360                     name : this.name
36361                 }
36362             ]
36363         };
36364         
36365         var labelCls = 'col-md-12';
36366         var contentCls = 'col-md-4';
36367         
36368         if(this.fieldLabel){
36369             
36370             var label = {
36371                 tag : 'div',
36372                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
36373                 cn : [
36374                     {
36375                         tag : 'label',
36376                         html : this.fieldLabel
36377                     }
36378                 ]
36379             };
36380             
36381             if(this.labelAlign == 'left'){
36382             
36383                 if(this.labelWidth > 12){
36384                     label.style = "width: " + this.labelWidth + 'px';
36385                 }
36386
36387                 if(this.labelWidth < 13 && this.labelmd == 0){
36388                     this.labelmd = this.labelWidth;
36389                 }
36390
36391                 if(this.labellg > 0){
36392                     labelCls = ' col-lg-' + this.labellg;
36393                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
36394                 }
36395
36396                 if(this.labelmd > 0){
36397                     labelCls = ' col-md-' + this.labelmd;
36398                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
36399                 }
36400
36401                 if(this.labelsm > 0){
36402                     labelCls = ' col-sm-' + this.labelsm;
36403                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
36404                 }
36405
36406                 if(this.labelxs > 0){
36407                     labelCls = ' col-xs-' + this.labelxs;
36408                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
36409                 }
36410             }
36411             
36412             label.cls += ' ' + labelCls;
36413             
36414             cfg.cn.push(label);
36415         }
36416         
36417         Roo.each(['day', 'month', 'year'], function(t){
36418             cfg.cn.push({
36419                 tag : 'div',
36420                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
36421             });
36422         }, this);
36423         
36424         return cfg;
36425     },
36426     
36427     inputEl: function ()
36428     {
36429         return this.el.select('.roo-date-split-field-group-value', true).first();
36430     },
36431     
36432     onRender : function(ct, position) 
36433     {
36434         var _this = this;
36435         
36436         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
36437         
36438         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
36439         
36440         this.dayField = new Roo.bootstrap.form.ComboBox({
36441             allowBlank : this.dayAllowBlank,
36442             alwaysQuery : true,
36443             displayField : 'value',
36444             editable : false,
36445             fieldLabel : '',
36446             forceSelection : true,
36447             mode : 'local',
36448             placeholder : this.dayPlaceholder,
36449             selectOnFocus : true,
36450             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36451             triggerAction : 'all',
36452             typeAhead : true,
36453             valueField : 'value',
36454             store : new Roo.data.SimpleStore({
36455                 data : (function() {    
36456                     var days = [];
36457                     _this.fireEvent('days', _this, days);
36458                     return days;
36459                 })(),
36460                 fields : [ 'value' ]
36461             }),
36462             listeners : {
36463                 select : function (_self, record, index)
36464                 {
36465                     _this.setValue(_this.getValue());
36466                 }
36467             }
36468         });
36469
36470         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
36471         
36472         this.monthField = new Roo.bootstrap.form.MonthField({
36473             after : '<i class=\"fa fa-calendar\"></i>',
36474             allowBlank : this.monthAllowBlank,
36475             placeholder : this.monthPlaceholder,
36476             readOnly : true,
36477             listeners : {
36478                 render : function (_self)
36479                 {
36480                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
36481                         e.preventDefault();
36482                         _self.focus();
36483                     });
36484                 },
36485                 select : function (_self, oldvalue, newvalue)
36486                 {
36487                     _this.setValue(_this.getValue());
36488                 }
36489             }
36490         });
36491         
36492         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
36493         
36494         this.yearField = new Roo.bootstrap.form.ComboBox({
36495             allowBlank : this.yearAllowBlank,
36496             alwaysQuery : true,
36497             displayField : 'value',
36498             editable : false,
36499             fieldLabel : '',
36500             forceSelection : true,
36501             mode : 'local',
36502             placeholder : this.yearPlaceholder,
36503             selectOnFocus : true,
36504             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36505             triggerAction : 'all',
36506             typeAhead : true,
36507             valueField : 'value',
36508             store : new Roo.data.SimpleStore({
36509                 data : (function() {
36510                     var years = [];
36511                     _this.fireEvent('years', _this, years);
36512                     return years;
36513                 })(),
36514                 fields : [ 'value' ]
36515             }),
36516             listeners : {
36517                 select : function (_self, record, index)
36518                 {
36519                     _this.setValue(_this.getValue());
36520                 }
36521             }
36522         });
36523
36524         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
36525     },
36526     
36527     setValue : function(v, format)
36528     {
36529         this.inputEl.dom.value = v;
36530         
36531         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
36532         
36533         var d = Date.parseDate(v, f);
36534         
36535         if(!d){
36536             this.validate();
36537             return;
36538         }
36539         
36540         this.setDay(d.format(this.dayFormat));
36541         this.setMonth(d.format(this.monthFormat));
36542         this.setYear(d.format(this.yearFormat));
36543         
36544         this.validate();
36545         
36546         return;
36547     },
36548     
36549     setDay : function(v)
36550     {
36551         this.dayField.setValue(v);
36552         this.inputEl.dom.value = this.getValue();
36553         this.validate();
36554         return;
36555     },
36556     
36557     setMonth : function(v)
36558     {
36559         this.monthField.setValue(v, true);
36560         this.inputEl.dom.value = this.getValue();
36561         this.validate();
36562         return;
36563     },
36564     
36565     setYear : function(v)
36566     {
36567         this.yearField.setValue(v);
36568         this.inputEl.dom.value = this.getValue();
36569         this.validate();
36570         return;
36571     },
36572     
36573     getDay : function()
36574     {
36575         return this.dayField.getValue();
36576     },
36577     
36578     getMonth : function()
36579     {
36580         return this.monthField.getValue();
36581     },
36582     
36583     getYear : function()
36584     {
36585         return this.yearField.getValue();
36586     },
36587     
36588     getValue : function()
36589     {
36590         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
36591         
36592         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
36593         
36594         return date;
36595     },
36596     
36597     reset : function()
36598     {
36599         this.setDay('');
36600         this.setMonth('');
36601         this.setYear('');
36602         this.inputEl.dom.value = '';
36603         this.validate();
36604         return;
36605     },
36606     
36607     validate : function()
36608     {
36609         var d = this.dayField.validate();
36610         var m = this.monthField.validate();
36611         var y = this.yearField.validate();
36612         
36613         var valid = true;
36614         
36615         if(
36616                 (!this.dayAllowBlank && !d) ||
36617                 (!this.monthAllowBlank && !m) ||
36618                 (!this.yearAllowBlank && !y)
36619         ){
36620             valid = false;
36621         }
36622         
36623         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
36624             return valid;
36625         }
36626         
36627         if(valid){
36628             this.markValid();
36629             return valid;
36630         }
36631         
36632         this.markInvalid();
36633         
36634         return valid;
36635     },
36636     
36637     markValid : function()
36638     {
36639         
36640         var label = this.el.select('label', true).first();
36641         var icon = this.el.select('i.fa-star', true).first();
36642
36643         if(label && icon){
36644             icon.remove();
36645         }
36646         
36647         this.fireEvent('valid', this);
36648     },
36649     
36650      /**
36651      * Mark this field as invalid
36652      * @param {String} msg The validation message
36653      */
36654     markInvalid : function(msg)
36655     {
36656         
36657         var label = this.el.select('label', true).first();
36658         var icon = this.el.select('i.fa-star', true).first();
36659
36660         if(label && !icon){
36661             this.el.select('.roo-date-split-field-label', true).createChild({
36662                 tag : 'i',
36663                 cls : 'text-danger fa fa-lg fa-star',
36664                 tooltip : 'This field is required',
36665                 style : 'margin-right:5px;'
36666             }, label, true);
36667         }
36668         
36669         this.fireEvent('invalid', this, msg);
36670     },
36671     
36672     clearInvalid : function()
36673     {
36674         var label = this.el.select('label', true).first();
36675         var icon = this.el.select('i.fa-star', true).first();
36676
36677         if(label && icon){
36678             icon.remove();
36679         }
36680         
36681         this.fireEvent('valid', this);
36682     },
36683     
36684     getName: function()
36685     {
36686         return this.name;
36687     }
36688     
36689 });
36690
36691  
36692
36693 /**
36694  * @class Roo.bootstrap.LayoutMasonry
36695  * @extends Roo.bootstrap.Component
36696  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
36697  * Bootstrap Layout Masonry class
36698  *
36699  * This is based on 
36700  * http://masonry.desandro.com
36701  *
36702  * The idea is to render all the bricks based on vertical width...
36703  *
36704  * The original code extends 'outlayer' - we might need to use that....
36705
36706  * @constructor
36707  * Create a new Element
36708  * @param {Object} config The config object
36709  */
36710
36711 Roo.bootstrap.LayoutMasonry = function(config){
36712     
36713     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
36714     
36715     this.bricks = [];
36716     
36717     Roo.bootstrap.LayoutMasonry.register(this);
36718     
36719     this.addEvents({
36720         // raw events
36721         /**
36722          * @event layout
36723          * Fire after layout the items
36724          * @param {Roo.bootstrap.LayoutMasonry} this
36725          * @param {Roo.EventObject} e
36726          */
36727         "layout" : true
36728     });
36729     
36730 };
36731
36732 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
36733     
36734     /**
36735      * @cfg {Boolean} isLayoutInstant = no animation?
36736      */   
36737     isLayoutInstant : false, // needed?
36738    
36739     /**
36740      * @cfg {Number} boxWidth  width of the columns
36741      */   
36742     boxWidth : 450,
36743     
36744       /**
36745      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
36746      */   
36747     boxHeight : 0,
36748     
36749     /**
36750      * @cfg {Number} padWidth padding below box..
36751      */   
36752     padWidth : 10, 
36753     
36754     /**
36755      * @cfg {Number} gutter gutter width..
36756      */   
36757     gutter : 10,
36758     
36759      /**
36760      * @cfg {Number} maxCols maximum number of columns
36761      */   
36762     
36763     maxCols: 0,
36764     
36765     /**
36766      * @cfg {Boolean} isAutoInitial defalut true
36767      */   
36768     isAutoInitial : true, 
36769     
36770     containerWidth: 0,
36771     
36772     /**
36773      * @cfg {Boolean} isHorizontal defalut false
36774      */   
36775     isHorizontal : false, 
36776
36777     currentSize : null,
36778     
36779     tag: 'div',
36780     
36781     cls: '',
36782     
36783     bricks: null, //CompositeElement
36784     
36785     cols : 1,
36786     
36787     _isLayoutInited : false,
36788     
36789 //    isAlternative : false, // only use for vertical layout...
36790     
36791     /**
36792      * @cfg {Number} alternativePadWidth padding below box..
36793      */   
36794     alternativePadWidth : 50,
36795     
36796     selectedBrick : [],
36797     
36798     getAutoCreate : function(){
36799         
36800         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
36801         
36802         var cfg = {
36803             tag: this.tag,
36804             cls: 'blog-masonary-wrapper ' + this.cls,
36805             cn : {
36806                 cls : 'mas-boxes masonary'
36807             }
36808         };
36809         
36810         return cfg;
36811     },
36812     
36813     getChildContainer: function( )
36814     {
36815         if (this.boxesEl) {
36816             return this.boxesEl;
36817         }
36818         
36819         this.boxesEl = this.el.select('.mas-boxes').first();
36820         
36821         return this.boxesEl;
36822     },
36823     
36824     
36825     initEvents : function()
36826     {
36827         var _this = this;
36828         
36829         if(this.isAutoInitial){
36830             Roo.log('hook children rendered');
36831             this.on('childrenrendered', function() {
36832                 Roo.log('children rendered');
36833                 _this.initial();
36834             } ,this);
36835         }
36836     },
36837     
36838     initial : function()
36839     {
36840         this.selectedBrick = [];
36841         
36842         this.currentSize = this.el.getBox(true);
36843         
36844         Roo.EventManager.onWindowResize(this.resize, this); 
36845
36846         if(!this.isAutoInitial){
36847             this.layout();
36848             return;
36849         }
36850         
36851         this.layout();
36852         
36853         return;
36854         //this.layout.defer(500,this);
36855         
36856     },
36857     
36858     resize : function()
36859     {
36860         var cs = this.el.getBox(true);
36861         
36862         if (
36863                 this.currentSize.width == cs.width && 
36864                 this.currentSize.x == cs.x && 
36865                 this.currentSize.height == cs.height && 
36866                 this.currentSize.y == cs.y 
36867         ) {
36868             Roo.log("no change in with or X or Y");
36869             return;
36870         }
36871         
36872         this.currentSize = cs;
36873         
36874         this.layout();
36875         
36876     },
36877     
36878     layout : function()
36879     {   
36880         this._resetLayout();
36881         
36882         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
36883         
36884         this.layoutItems( isInstant );
36885       
36886         this._isLayoutInited = true;
36887         
36888         this.fireEvent('layout', this);
36889         
36890     },
36891     
36892     _resetLayout : function()
36893     {
36894         if(this.isHorizontal){
36895             this.horizontalMeasureColumns();
36896             return;
36897         }
36898         
36899         this.verticalMeasureColumns();
36900         
36901     },
36902     
36903     verticalMeasureColumns : function()
36904     {
36905         this.getContainerWidth();
36906         
36907 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
36908 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
36909 //            return;
36910 //        }
36911         
36912         var boxWidth = this.boxWidth + this.padWidth;
36913         
36914         if(this.containerWidth < this.boxWidth){
36915             boxWidth = this.containerWidth
36916         }
36917         
36918         var containerWidth = this.containerWidth;
36919         
36920         var cols = Math.floor(containerWidth / boxWidth);
36921         
36922         this.cols = Math.max( cols, 1 );
36923         
36924         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36925         
36926         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
36927         
36928         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
36929         
36930         this.colWidth = boxWidth + avail - this.padWidth;
36931         
36932         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
36933         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
36934     },
36935     
36936     horizontalMeasureColumns : function()
36937     {
36938         this.getContainerWidth();
36939         
36940         var boxWidth = this.boxWidth;
36941         
36942         if(this.containerWidth < boxWidth){
36943             boxWidth = this.containerWidth;
36944         }
36945         
36946         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
36947         
36948         this.el.setHeight(boxWidth);
36949         
36950     },
36951     
36952     getContainerWidth : function()
36953     {
36954         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36955     },
36956     
36957     layoutItems : function( isInstant )
36958     {
36959         Roo.log(this.bricks);
36960         
36961         var items = Roo.apply([], this.bricks);
36962         
36963         if(this.isHorizontal){
36964             this._horizontalLayoutItems( items , isInstant );
36965             return;
36966         }
36967         
36968 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
36969 //            this._verticalAlternativeLayoutItems( items , isInstant );
36970 //            return;
36971 //        }
36972         
36973         this._verticalLayoutItems( items , isInstant );
36974         
36975     },
36976     
36977     _verticalLayoutItems : function ( items , isInstant)
36978     {
36979         if ( !items || !items.length ) {
36980             return;
36981         }
36982         
36983         var standard = [
36984             ['xs', 'xs', 'xs', 'tall'],
36985             ['xs', 'xs', 'tall'],
36986             ['xs', 'xs', 'sm'],
36987             ['xs', 'xs', 'xs'],
36988             ['xs', 'tall'],
36989             ['xs', 'sm'],
36990             ['xs', 'xs'],
36991             ['xs'],
36992             
36993             ['sm', 'xs', 'xs'],
36994             ['sm', 'xs'],
36995             ['sm'],
36996             
36997             ['tall', 'xs', 'xs', 'xs'],
36998             ['tall', 'xs', 'xs'],
36999             ['tall', 'xs'],
37000             ['tall']
37001             
37002         ];
37003         
37004         var queue = [];
37005         
37006         var boxes = [];
37007         
37008         var box = [];
37009         
37010         Roo.each(items, function(item, k){
37011             
37012             switch (item.size) {
37013                 // these layouts take up a full box,
37014                 case 'md' :
37015                 case 'md-left' :
37016                 case 'md-right' :
37017                 case 'wide' :
37018                     
37019                     if(box.length){
37020                         boxes.push(box);
37021                         box = [];
37022                     }
37023                     
37024                     boxes.push([item]);
37025                     
37026                     break;
37027                     
37028                 case 'xs' :
37029                 case 'sm' :
37030                 case 'tall' :
37031                     
37032                     box.push(item);
37033                     
37034                     break;
37035                 default :
37036                     break;
37037                     
37038             }
37039             
37040         }, this);
37041         
37042         if(box.length){
37043             boxes.push(box);
37044             box = [];
37045         }
37046         
37047         var filterPattern = function(box, length)
37048         {
37049             if(!box.length){
37050                 return;
37051             }
37052             
37053             var match = false;
37054             
37055             var pattern = box.slice(0, length);
37056             
37057             var format = [];
37058             
37059             Roo.each(pattern, function(i){
37060                 format.push(i.size);
37061             }, this);
37062             
37063             Roo.each(standard, function(s){
37064                 
37065                 if(String(s) != String(format)){
37066                     return;
37067                 }
37068                 
37069                 match = true;
37070                 return false;
37071                 
37072             }, this);
37073             
37074             if(!match && length == 1){
37075                 return;
37076             }
37077             
37078             if(!match){
37079                 filterPattern(box, length - 1);
37080                 return;
37081             }
37082                 
37083             queue.push(pattern);
37084
37085             box = box.slice(length, box.length);
37086
37087             filterPattern(box, 4);
37088
37089             return;
37090             
37091         }
37092         
37093         Roo.each(boxes, function(box, k){
37094             
37095             if(!box.length){
37096                 return;
37097             }
37098             
37099             if(box.length == 1){
37100                 queue.push(box);
37101                 return;
37102             }
37103             
37104             filterPattern(box, 4);
37105             
37106         }, this);
37107         
37108         this._processVerticalLayoutQueue( queue, isInstant );
37109         
37110     },
37111     
37112 //    _verticalAlternativeLayoutItems : function( items , isInstant )
37113 //    {
37114 //        if ( !items || !items.length ) {
37115 //            return;
37116 //        }
37117 //
37118 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
37119 //        
37120 //    },
37121     
37122     _horizontalLayoutItems : function ( items , isInstant)
37123     {
37124         if ( !items || !items.length || items.length < 3) {
37125             return;
37126         }
37127         
37128         items.reverse();
37129         
37130         var eItems = items.slice(0, 3);
37131         
37132         items = items.slice(3, items.length);
37133         
37134         var standard = [
37135             ['xs', 'xs', 'xs', 'wide'],
37136             ['xs', 'xs', 'wide'],
37137             ['xs', 'xs', 'sm'],
37138             ['xs', 'xs', 'xs'],
37139             ['xs', 'wide'],
37140             ['xs', 'sm'],
37141             ['xs', 'xs'],
37142             ['xs'],
37143             
37144             ['sm', 'xs', 'xs'],
37145             ['sm', 'xs'],
37146             ['sm'],
37147             
37148             ['wide', 'xs', 'xs', 'xs'],
37149             ['wide', 'xs', 'xs'],
37150             ['wide', 'xs'],
37151             ['wide'],
37152             
37153             ['wide-thin']
37154         ];
37155         
37156         var queue = [];
37157         
37158         var boxes = [];
37159         
37160         var box = [];
37161         
37162         Roo.each(items, function(item, k){
37163             
37164             switch (item.size) {
37165                 case 'md' :
37166                 case 'md-left' :
37167                 case 'md-right' :
37168                 case 'tall' :
37169                     
37170                     if(box.length){
37171                         boxes.push(box);
37172                         box = [];
37173                     }
37174                     
37175                     boxes.push([item]);
37176                     
37177                     break;
37178                     
37179                 case 'xs' :
37180                 case 'sm' :
37181                 case 'wide' :
37182                 case 'wide-thin' :
37183                     
37184                     box.push(item);
37185                     
37186                     break;
37187                 default :
37188                     break;
37189                     
37190             }
37191             
37192         }, this);
37193         
37194         if(box.length){
37195             boxes.push(box);
37196             box = [];
37197         }
37198         
37199         var filterPattern = function(box, length)
37200         {
37201             if(!box.length){
37202                 return;
37203             }
37204             
37205             var match = false;
37206             
37207             var pattern = box.slice(0, length);
37208             
37209             var format = [];
37210             
37211             Roo.each(pattern, function(i){
37212                 format.push(i.size);
37213             }, this);
37214             
37215             Roo.each(standard, function(s){
37216                 
37217                 if(String(s) != String(format)){
37218                     return;
37219                 }
37220                 
37221                 match = true;
37222                 return false;
37223                 
37224             }, this);
37225             
37226             if(!match && length == 1){
37227                 return;
37228             }
37229             
37230             if(!match){
37231                 filterPattern(box, length - 1);
37232                 return;
37233             }
37234                 
37235             queue.push(pattern);
37236
37237             box = box.slice(length, box.length);
37238
37239             filterPattern(box, 4);
37240
37241             return;
37242             
37243         }
37244         
37245         Roo.each(boxes, function(box, k){
37246             
37247             if(!box.length){
37248                 return;
37249             }
37250             
37251             if(box.length == 1){
37252                 queue.push(box);
37253                 return;
37254             }
37255             
37256             filterPattern(box, 4);
37257             
37258         }, this);
37259         
37260         
37261         var prune = [];
37262         
37263         var pos = this.el.getBox(true);
37264         
37265         var minX = pos.x;
37266         
37267         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37268         
37269         var hit_end = false;
37270         
37271         Roo.each(queue, function(box){
37272             
37273             if(hit_end){
37274                 
37275                 Roo.each(box, function(b){
37276                 
37277                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
37278                     b.el.hide();
37279
37280                 }, this);
37281
37282                 return;
37283             }
37284             
37285             var mx = 0;
37286             
37287             Roo.each(box, function(b){
37288                 
37289                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
37290                 b.el.show();
37291
37292                 mx = Math.max(mx, b.x);
37293                 
37294             }, this);
37295             
37296             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
37297             
37298             if(maxX < minX){
37299                 
37300                 Roo.each(box, function(b){
37301                 
37302                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
37303                     b.el.hide();
37304                     
37305                 }, this);
37306                 
37307                 hit_end = true;
37308                 
37309                 return;
37310             }
37311             
37312             prune.push(box);
37313             
37314         }, this);
37315         
37316         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
37317     },
37318     
37319     /** Sets position of item in DOM
37320     * @param {Element} item
37321     * @param {Number} x - horizontal position
37322     * @param {Number} y - vertical position
37323     * @param {Boolean} isInstant - disables transitions
37324     */
37325     _processVerticalLayoutQueue : function( queue, isInstant )
37326     {
37327         var pos = this.el.getBox(true);
37328         var x = pos.x;
37329         var y = pos.y;
37330         var maxY = [];
37331         
37332         for (var i = 0; i < this.cols; i++){
37333             maxY[i] = pos.y;
37334         }
37335         
37336         Roo.each(queue, function(box, k){
37337             
37338             var col = k % this.cols;
37339             
37340             Roo.each(box, function(b,kk){
37341                 
37342                 b.el.position('absolute');
37343                 
37344                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37345                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37346                 
37347                 if(b.size == 'md-left' || b.size == 'md-right'){
37348                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37349                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37350                 }
37351                 
37352                 b.el.setWidth(width);
37353                 b.el.setHeight(height);
37354                 // iframe?
37355                 b.el.select('iframe',true).setSize(width,height);
37356                 
37357             }, this);
37358             
37359             for (var i = 0; i < this.cols; i++){
37360                 
37361                 if(maxY[i] < maxY[col]){
37362                     col = i;
37363                     continue;
37364                 }
37365                 
37366                 col = Math.min(col, i);
37367                 
37368             }
37369             
37370             x = pos.x + col * (this.colWidth + this.padWidth);
37371             
37372             y = maxY[col];
37373             
37374             var positions = [];
37375             
37376             switch (box.length){
37377                 case 1 :
37378                     positions = this.getVerticalOneBoxColPositions(x, y, box);
37379                     break;
37380                 case 2 :
37381                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
37382                     break;
37383                 case 3 :
37384                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
37385                     break;
37386                 case 4 :
37387                     positions = this.getVerticalFourBoxColPositions(x, y, box);
37388                     break;
37389                 default :
37390                     break;
37391             }
37392             
37393             Roo.each(box, function(b,kk){
37394                 
37395                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37396                 
37397                 var sz = b.el.getSize();
37398                 
37399                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
37400                 
37401             }, this);
37402             
37403         }, this);
37404         
37405         var mY = 0;
37406         
37407         for (var i = 0; i < this.cols; i++){
37408             mY = Math.max(mY, maxY[i]);
37409         }
37410         
37411         this.el.setHeight(mY - pos.y);
37412         
37413     },
37414     
37415 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
37416 //    {
37417 //        var pos = this.el.getBox(true);
37418 //        var x = pos.x;
37419 //        var y = pos.y;
37420 //        var maxX = pos.right;
37421 //        
37422 //        var maxHeight = 0;
37423 //        
37424 //        Roo.each(items, function(item, k){
37425 //            
37426 //            var c = k % 2;
37427 //            
37428 //            item.el.position('absolute');
37429 //                
37430 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
37431 //
37432 //            item.el.setWidth(width);
37433 //
37434 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
37435 //
37436 //            item.el.setHeight(height);
37437 //            
37438 //            if(c == 0){
37439 //                item.el.setXY([x, y], isInstant ? false : true);
37440 //            } else {
37441 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
37442 //            }
37443 //            
37444 //            y = y + height + this.alternativePadWidth;
37445 //            
37446 //            maxHeight = maxHeight + height + this.alternativePadWidth;
37447 //            
37448 //        }, this);
37449 //        
37450 //        this.el.setHeight(maxHeight);
37451 //        
37452 //    },
37453     
37454     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
37455     {
37456         var pos = this.el.getBox(true);
37457         
37458         var minX = pos.x;
37459         var minY = pos.y;
37460         
37461         var maxX = pos.right;
37462         
37463         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
37464         
37465         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37466         
37467         Roo.each(queue, function(box, k){
37468             
37469             Roo.each(box, function(b, kk){
37470                 
37471                 b.el.position('absolute');
37472                 
37473                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37474                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37475                 
37476                 if(b.size == 'md-left' || b.size == 'md-right'){
37477                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37478                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37479                 }
37480                 
37481                 b.el.setWidth(width);
37482                 b.el.setHeight(height);
37483                 
37484             }, this);
37485             
37486             if(!box.length){
37487                 return;
37488             }
37489             
37490             var positions = [];
37491             
37492             switch (box.length){
37493                 case 1 :
37494                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
37495                     break;
37496                 case 2 :
37497                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
37498                     break;
37499                 case 3 :
37500                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
37501                     break;
37502                 case 4 :
37503                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
37504                     break;
37505                 default :
37506                     break;
37507             }
37508             
37509             Roo.each(box, function(b,kk){
37510                 
37511                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37512                 
37513                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
37514                 
37515             }, this);
37516             
37517         }, this);
37518         
37519     },
37520     
37521     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
37522     {
37523         Roo.each(eItems, function(b,k){
37524             
37525             b.size = (k == 0) ? 'sm' : 'xs';
37526             b.x = (k == 0) ? 2 : 1;
37527             b.y = (k == 0) ? 2 : 1;
37528             
37529             b.el.position('absolute');
37530             
37531             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37532                 
37533             b.el.setWidth(width);
37534             
37535             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37536             
37537             b.el.setHeight(height);
37538             
37539         }, this);
37540
37541         var positions = [];
37542         
37543         positions.push({
37544             x : maxX - this.unitWidth * 2 - this.gutter,
37545             y : minY
37546         });
37547         
37548         positions.push({
37549             x : maxX - this.unitWidth,
37550             y : minY + (this.unitWidth + this.gutter) * 2
37551         });
37552         
37553         positions.push({
37554             x : maxX - this.unitWidth * 3 - this.gutter * 2,
37555             y : minY
37556         });
37557         
37558         Roo.each(eItems, function(b,k){
37559             
37560             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
37561
37562         }, this);
37563         
37564     },
37565     
37566     getVerticalOneBoxColPositions : function(x, y, box)
37567     {
37568         var pos = [];
37569         
37570         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
37571         
37572         if(box[0].size == 'md-left'){
37573             rand = 0;
37574         }
37575         
37576         if(box[0].size == 'md-right'){
37577             rand = 1;
37578         }
37579         
37580         pos.push({
37581             x : x + (this.unitWidth + this.gutter) * rand,
37582             y : y
37583         });
37584         
37585         return pos;
37586     },
37587     
37588     getVerticalTwoBoxColPositions : function(x, y, box)
37589     {
37590         var pos = [];
37591         
37592         if(box[0].size == 'xs'){
37593             
37594             pos.push({
37595                 x : x,
37596                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
37597             });
37598
37599             pos.push({
37600                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
37601                 y : y
37602             });
37603             
37604             return pos;
37605             
37606         }
37607         
37608         pos.push({
37609             x : x,
37610             y : y
37611         });
37612
37613         pos.push({
37614             x : x + (this.unitWidth + this.gutter) * 2,
37615             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
37616         });
37617         
37618         return pos;
37619         
37620     },
37621     
37622     getVerticalThreeBoxColPositions : function(x, y, box)
37623     {
37624         var pos = [];
37625         
37626         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
37627             
37628             pos.push({
37629                 x : x,
37630                 y : y
37631             });
37632
37633             pos.push({
37634                 x : x + (this.unitWidth + this.gutter) * 1,
37635                 y : y
37636             });
37637             
37638             pos.push({
37639                 x : x + (this.unitWidth + this.gutter) * 2,
37640                 y : y
37641             });
37642             
37643             return pos;
37644             
37645         }
37646         
37647         if(box[0].size == 'xs' && box[1].size == 'xs'){
37648             
37649             pos.push({
37650                 x : x,
37651                 y : y
37652             });
37653
37654             pos.push({
37655                 x : x,
37656                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
37657             });
37658             
37659             pos.push({
37660                 x : x + (this.unitWidth + this.gutter) * 1,
37661                 y : y
37662             });
37663             
37664             return pos;
37665             
37666         }
37667         
37668         pos.push({
37669             x : x,
37670             y : y
37671         });
37672
37673         pos.push({
37674             x : x + (this.unitWidth + this.gutter) * 2,
37675             y : y
37676         });
37677
37678         pos.push({
37679             x : x + (this.unitWidth + this.gutter) * 2,
37680             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
37681         });
37682             
37683         return pos;
37684         
37685     },
37686     
37687     getVerticalFourBoxColPositions : function(x, y, box)
37688     {
37689         var pos = [];
37690         
37691         if(box[0].size == 'xs'){
37692             
37693             pos.push({
37694                 x : x,
37695                 y : y
37696             });
37697
37698             pos.push({
37699                 x : x,
37700                 y : y + (this.unitHeight + this.gutter) * 1
37701             });
37702             
37703             pos.push({
37704                 x : x,
37705                 y : y + (this.unitHeight + this.gutter) * 2
37706             });
37707             
37708             pos.push({
37709                 x : x + (this.unitWidth + this.gutter) * 1,
37710                 y : y
37711             });
37712             
37713             return pos;
37714             
37715         }
37716         
37717         pos.push({
37718             x : x,
37719             y : y
37720         });
37721
37722         pos.push({
37723             x : x + (this.unitWidth + this.gutter) * 2,
37724             y : y
37725         });
37726
37727         pos.push({
37728             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
37729             y : y + (this.unitHeight + this.gutter) * 1
37730         });
37731
37732         pos.push({
37733             x : x + (this.unitWidth + this.gutter) * 2,
37734             y : y + (this.unitWidth + this.gutter) * 2
37735         });
37736
37737         return pos;
37738         
37739     },
37740     
37741     getHorizontalOneBoxColPositions : function(maxX, minY, box)
37742     {
37743         var pos = [];
37744         
37745         if(box[0].size == 'md-left'){
37746             pos.push({
37747                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37748                 y : minY
37749             });
37750             
37751             return pos;
37752         }
37753         
37754         if(box[0].size == 'md-right'){
37755             pos.push({
37756                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37757                 y : minY + (this.unitWidth + this.gutter) * 1
37758             });
37759             
37760             return pos;
37761         }
37762         
37763         var rand = Math.floor(Math.random() * (4 - box[0].y));
37764         
37765         pos.push({
37766             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37767             y : minY + (this.unitWidth + this.gutter) * rand
37768         });
37769         
37770         return pos;
37771         
37772     },
37773     
37774     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
37775     {
37776         var pos = [];
37777         
37778         if(box[0].size == 'xs'){
37779             
37780             pos.push({
37781                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37782                 y : minY
37783             });
37784
37785             pos.push({
37786                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37787                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
37788             });
37789             
37790             return pos;
37791             
37792         }
37793         
37794         pos.push({
37795             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37796             y : minY
37797         });
37798
37799         pos.push({
37800             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37801             y : minY + (this.unitWidth + this.gutter) * 2
37802         });
37803         
37804         return pos;
37805         
37806     },
37807     
37808     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
37809     {
37810         var pos = [];
37811         
37812         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
37813             
37814             pos.push({
37815                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37816                 y : minY
37817             });
37818
37819             pos.push({
37820                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37821                 y : minY + (this.unitWidth + this.gutter) * 1
37822             });
37823             
37824             pos.push({
37825                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37826                 y : minY + (this.unitWidth + this.gutter) * 2
37827             });
37828             
37829             return pos;
37830             
37831         }
37832         
37833         if(box[0].size == 'xs' && box[1].size == 'xs'){
37834             
37835             pos.push({
37836                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37837                 y : minY
37838             });
37839
37840             pos.push({
37841                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37842                 y : minY
37843             });
37844             
37845             pos.push({
37846                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37847                 y : minY + (this.unitWidth + this.gutter) * 1
37848             });
37849             
37850             return pos;
37851             
37852         }
37853         
37854         pos.push({
37855             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37856             y : minY
37857         });
37858
37859         pos.push({
37860             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37861             y : minY + (this.unitWidth + this.gutter) * 2
37862         });
37863
37864         pos.push({
37865             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37866             y : minY + (this.unitWidth + this.gutter) * 2
37867         });
37868             
37869         return pos;
37870         
37871     },
37872     
37873     getHorizontalFourBoxColPositions : function(maxX, minY, box)
37874     {
37875         var pos = [];
37876         
37877         if(box[0].size == 'xs'){
37878             
37879             pos.push({
37880                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37881                 y : minY
37882             });
37883
37884             pos.push({
37885                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37886                 y : minY
37887             });
37888             
37889             pos.push({
37890                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37891                 y : minY
37892             });
37893             
37894             pos.push({
37895                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
37896                 y : minY + (this.unitWidth + this.gutter) * 1
37897             });
37898             
37899             return pos;
37900             
37901         }
37902         
37903         pos.push({
37904             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37905             y : minY
37906         });
37907         
37908         pos.push({
37909             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37910             y : minY + (this.unitWidth + this.gutter) * 2
37911         });
37912         
37913         pos.push({
37914             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37915             y : minY + (this.unitWidth + this.gutter) * 2
37916         });
37917         
37918         pos.push({
37919             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
37920             y : minY + (this.unitWidth + this.gutter) * 2
37921         });
37922
37923         return pos;
37924         
37925     },
37926     
37927     /**
37928     * remove a Masonry Brick
37929     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
37930     */
37931     removeBrick : function(brick_id)
37932     {
37933         if (!brick_id) {
37934             return;
37935         }
37936         
37937         for (var i = 0; i<this.bricks.length; i++) {
37938             if (this.bricks[i].id == brick_id) {
37939                 this.bricks.splice(i,1);
37940                 this.el.dom.removeChild(Roo.get(brick_id).dom);
37941                 this.initial();
37942             }
37943         }
37944     },
37945     
37946     /**
37947     * adds a Masonry Brick
37948     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
37949     */
37950     addBrick : function(cfg)
37951     {
37952         var cn = new Roo.bootstrap.MasonryBrick(cfg);
37953         //this.register(cn);
37954         cn.parentId = this.id;
37955         cn.render(this.el);
37956         return cn;
37957     },
37958     
37959     /**
37960     * register a Masonry Brick
37961     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
37962     */
37963     
37964     register : function(brick)
37965     {
37966         this.bricks.push(brick);
37967         brick.masonryId = this.id;
37968     },
37969     
37970     /**
37971     * clear all the Masonry Brick
37972     */
37973     clearAll : function()
37974     {
37975         this.bricks = [];
37976         //this.getChildContainer().dom.innerHTML = "";
37977         this.el.dom.innerHTML = '';
37978     },
37979     
37980     getSelected : function()
37981     {
37982         if (!this.selectedBrick) {
37983             return false;
37984         }
37985         
37986         return this.selectedBrick;
37987     }
37988 });
37989
37990 Roo.apply(Roo.bootstrap.LayoutMasonry, {
37991     
37992     groups: {},
37993      /**
37994     * register a Masonry Layout
37995     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
37996     */
37997     
37998     register : function(layout)
37999     {
38000         this.groups[layout.id] = layout;
38001     },
38002     /**
38003     * fetch a  Masonry Layout based on the masonry layout ID
38004     * @param {string} the masonry layout to add
38005     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
38006     */
38007     
38008     get: function(layout_id) {
38009         if (typeof(this.groups[layout_id]) == 'undefined') {
38010             return false;
38011         }
38012         return this.groups[layout_id] ;
38013     }
38014     
38015     
38016     
38017 });
38018
38019  
38020
38021  /**
38022  *
38023  * This is based on 
38024  * http://masonry.desandro.com
38025  *
38026  * The idea is to render all the bricks based on vertical width...
38027  *
38028  * The original code extends 'outlayer' - we might need to use that....
38029  * 
38030  */
38031
38032
38033 /**
38034  * @class Roo.bootstrap.LayoutMasonryAuto
38035  * @extends Roo.bootstrap.Component
38036  * Bootstrap Layout Masonry class
38037  * 
38038  * @constructor
38039  * Create a new Element
38040  * @param {Object} config The config object
38041  */
38042
38043 Roo.bootstrap.LayoutMasonryAuto = function(config){
38044     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
38045 };
38046
38047 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
38048     
38049       /**
38050      * @cfg {Boolean} isFitWidth  - resize the width..
38051      */   
38052     isFitWidth : false,  // options..
38053     /**
38054      * @cfg {Boolean} isOriginLeft = left align?
38055      */   
38056     isOriginLeft : true,
38057     /**
38058      * @cfg {Boolean} isOriginTop = top align?
38059      */   
38060     isOriginTop : false,
38061     /**
38062      * @cfg {Boolean} isLayoutInstant = no animation?
38063      */   
38064     isLayoutInstant : false, // needed?
38065     /**
38066      * @cfg {Boolean} isResizingContainer = not sure if this is used..
38067      */   
38068     isResizingContainer : true,
38069     /**
38070      * @cfg {Number} columnWidth  width of the columns 
38071      */   
38072     
38073     columnWidth : 0,
38074     
38075     /**
38076      * @cfg {Number} maxCols maximum number of columns
38077      */   
38078     
38079     maxCols: 0,
38080     /**
38081      * @cfg {Number} padHeight padding below box..
38082      */   
38083     
38084     padHeight : 10, 
38085     
38086     /**
38087      * @cfg {Boolean} isAutoInitial defalut true
38088      */   
38089     
38090     isAutoInitial : true, 
38091     
38092     // private?
38093     gutter : 0,
38094     
38095     containerWidth: 0,
38096     initialColumnWidth : 0,
38097     currentSize : null,
38098     
38099     colYs : null, // array.
38100     maxY : 0,
38101     padWidth: 10,
38102     
38103     
38104     tag: 'div',
38105     cls: '',
38106     bricks: null, //CompositeElement
38107     cols : 0, // array?
38108     // element : null, // wrapped now this.el
38109     _isLayoutInited : null, 
38110     
38111     
38112     getAutoCreate : function(){
38113         
38114         var cfg = {
38115             tag: this.tag,
38116             cls: 'blog-masonary-wrapper ' + this.cls,
38117             cn : {
38118                 cls : 'mas-boxes masonary'
38119             }
38120         };
38121         
38122         return cfg;
38123     },
38124     
38125     getChildContainer: function( )
38126     {
38127         if (this.boxesEl) {
38128             return this.boxesEl;
38129         }
38130         
38131         this.boxesEl = this.el.select('.mas-boxes').first();
38132         
38133         return this.boxesEl;
38134     },
38135     
38136     
38137     initEvents : function()
38138     {
38139         var _this = this;
38140         
38141         if(this.isAutoInitial){
38142             Roo.log('hook children rendered');
38143             this.on('childrenrendered', function() {
38144                 Roo.log('children rendered');
38145                 _this.initial();
38146             } ,this);
38147         }
38148         
38149     },
38150     
38151     initial : function()
38152     {
38153         this.reloadItems();
38154
38155         this.currentSize = this.el.getBox(true);
38156
38157         /// was window resize... - let's see if this works..
38158         Roo.EventManager.onWindowResize(this.resize, this); 
38159
38160         if(!this.isAutoInitial){
38161             this.layout();
38162             return;
38163         }
38164         
38165         this.layout.defer(500,this);
38166     },
38167     
38168     reloadItems: function()
38169     {
38170         this.bricks = this.el.select('.masonry-brick', true);
38171         
38172         this.bricks.each(function(b) {
38173             //Roo.log(b.getSize());
38174             if (!b.attr('originalwidth')) {
38175                 b.attr('originalwidth',  b.getSize().width);
38176             }
38177             
38178         });
38179         
38180         Roo.log(this.bricks.elements.length);
38181     },
38182     
38183     resize : function()
38184     {
38185         Roo.log('resize');
38186         var cs = this.el.getBox(true);
38187         
38188         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
38189             Roo.log("no change in with or X");
38190             return;
38191         }
38192         this.currentSize = cs;
38193         this.layout();
38194     },
38195     
38196     layout : function()
38197     {
38198          Roo.log('layout');
38199         this._resetLayout();
38200         //this._manageStamps();
38201       
38202         // don't animate first layout
38203         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38204         this.layoutItems( isInstant );
38205       
38206         // flag for initalized
38207         this._isLayoutInited = true;
38208     },
38209     
38210     layoutItems : function( isInstant )
38211     {
38212         //var items = this._getItemsForLayout( this.items );
38213         // original code supports filtering layout items.. we just ignore it..
38214         
38215         this._layoutItems( this.bricks , isInstant );
38216       
38217         this._postLayout();
38218     },
38219     _layoutItems : function ( items , isInstant)
38220     {
38221        //this.fireEvent( 'layout', this, items );
38222     
38223
38224         if ( !items || !items.elements.length ) {
38225           // no items, emit event with empty array
38226             return;
38227         }
38228
38229         var queue = [];
38230         items.each(function(item) {
38231             Roo.log("layout item");
38232             Roo.log(item);
38233             // get x/y object from method
38234             var position = this._getItemLayoutPosition( item );
38235             // enqueue
38236             position.item = item;
38237             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
38238             queue.push( position );
38239         }, this);
38240       
38241         this._processLayoutQueue( queue );
38242     },
38243     /** Sets position of item in DOM
38244     * @param {Element} item
38245     * @param {Number} x - horizontal position
38246     * @param {Number} y - vertical position
38247     * @param {Boolean} isInstant - disables transitions
38248     */
38249     _processLayoutQueue : function( queue )
38250     {
38251         for ( var i=0, len = queue.length; i < len; i++ ) {
38252             var obj = queue[i];
38253             obj.item.position('absolute');
38254             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
38255         }
38256     },
38257       
38258     
38259     /**
38260     * Any logic you want to do after each layout,
38261     * i.e. size the container
38262     */
38263     _postLayout : function()
38264     {
38265         this.resizeContainer();
38266     },
38267     
38268     resizeContainer : function()
38269     {
38270         if ( !this.isResizingContainer ) {
38271             return;
38272         }
38273         var size = this._getContainerSize();
38274         if ( size ) {
38275             this.el.setSize(size.width,size.height);
38276             this.boxesEl.setSize(size.width,size.height);
38277         }
38278     },
38279     
38280     
38281     
38282     _resetLayout : function()
38283     {
38284         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
38285         this.colWidth = this.el.getWidth();
38286         //this.gutter = this.el.getWidth(); 
38287         
38288         this.measureColumns();
38289
38290         // reset column Y
38291         var i = this.cols;
38292         this.colYs = [];
38293         while (i--) {
38294             this.colYs.push( 0 );
38295         }
38296     
38297         this.maxY = 0;
38298     },
38299
38300     measureColumns : function()
38301     {
38302         this.getContainerWidth();
38303       // if columnWidth is 0, default to outerWidth of first item
38304         if ( !this.columnWidth ) {
38305             var firstItem = this.bricks.first();
38306             Roo.log(firstItem);
38307             this.columnWidth  = this.containerWidth;
38308             if (firstItem && firstItem.attr('originalwidth') ) {
38309                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
38310             }
38311             // columnWidth fall back to item of first element
38312             Roo.log("set column width?");
38313                         this.initialColumnWidth = this.columnWidth  ;
38314
38315             // if first elem has no width, default to size of container
38316             
38317         }
38318         
38319         
38320         if (this.initialColumnWidth) {
38321             this.columnWidth = this.initialColumnWidth;
38322         }
38323         
38324         
38325             
38326         // column width is fixed at the top - however if container width get's smaller we should
38327         // reduce it...
38328         
38329         // this bit calcs how man columns..
38330             
38331         var columnWidth = this.columnWidth += this.gutter;
38332       
38333         // calculate columns
38334         var containerWidth = this.containerWidth + this.gutter;
38335         
38336         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
38337         // fix rounding errors, typically with gutters
38338         var excess = columnWidth - containerWidth % columnWidth;
38339         
38340         
38341         // if overshoot is less than a pixel, round up, otherwise floor it
38342         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
38343         cols = Math[ mathMethod ]( cols );
38344         this.cols = Math.max( cols, 1 );
38345         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38346         
38347          // padding positioning..
38348         var totalColWidth = this.cols * this.columnWidth;
38349         var padavail = this.containerWidth - totalColWidth;
38350         // so for 2 columns - we need 3 'pads'
38351         
38352         var padNeeded = (1+this.cols) * this.padWidth;
38353         
38354         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
38355         
38356         this.columnWidth += padExtra
38357         //this.padWidth = Math.floor(padavail /  ( this.cols));
38358         
38359         // adjust colum width so that padding is fixed??
38360         
38361         // we have 3 columns ... total = width * 3
38362         // we have X left over... that should be used by 
38363         
38364         //if (this.expandC) {
38365             
38366         //}
38367         
38368         
38369         
38370     },
38371     
38372     getContainerWidth : function()
38373     {
38374        /* // container is parent if fit width
38375         var container = this.isFitWidth ? this.element.parentNode : this.element;
38376         // check that this.size and size are there
38377         // IE8 triggers resize on body size change, so they might not be
38378         
38379         var size = getSize( container );  //FIXME
38380         this.containerWidth = size && size.innerWidth; //FIXME
38381         */
38382          
38383         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
38384         
38385     },
38386     
38387     _getItemLayoutPosition : function( item )  // what is item?
38388     {
38389         // we resize the item to our columnWidth..
38390       
38391         item.setWidth(this.columnWidth);
38392         item.autoBoxAdjust  = false;
38393         
38394         var sz = item.getSize();
38395  
38396         // how many columns does this brick span
38397         var remainder = this.containerWidth % this.columnWidth;
38398         
38399         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
38400         // round if off by 1 pixel, otherwise use ceil
38401         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
38402         colSpan = Math.min( colSpan, this.cols );
38403         
38404         // normally this should be '1' as we dont' currently allow multi width columns..
38405         
38406         var colGroup = this._getColGroup( colSpan );
38407         // get the minimum Y value from the columns
38408         var minimumY = Math.min.apply( Math, colGroup );
38409         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
38410         
38411         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
38412          
38413         // position the brick
38414         var position = {
38415             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
38416             y: this.currentSize.y + minimumY + this.padHeight
38417         };
38418         
38419         Roo.log(position);
38420         // apply setHeight to necessary columns
38421         var setHeight = minimumY + sz.height + this.padHeight;
38422         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
38423         
38424         var setSpan = this.cols + 1 - colGroup.length;
38425         for ( var i = 0; i < setSpan; i++ ) {
38426           this.colYs[ shortColIndex + i ] = setHeight ;
38427         }
38428       
38429         return position;
38430     },
38431     
38432     /**
38433      * @param {Number} colSpan - number of columns the element spans
38434      * @returns {Array} colGroup
38435      */
38436     _getColGroup : function( colSpan )
38437     {
38438         if ( colSpan < 2 ) {
38439           // if brick spans only one column, use all the column Ys
38440           return this.colYs;
38441         }
38442       
38443         var colGroup = [];
38444         // how many different places could this brick fit horizontally
38445         var groupCount = this.cols + 1 - colSpan;
38446         // for each group potential horizontal position
38447         for ( var i = 0; i < groupCount; i++ ) {
38448           // make an array of colY values for that one group
38449           var groupColYs = this.colYs.slice( i, i + colSpan );
38450           // and get the max value of the array
38451           colGroup[i] = Math.max.apply( Math, groupColYs );
38452         }
38453         return colGroup;
38454     },
38455     /*
38456     _manageStamp : function( stamp )
38457     {
38458         var stampSize =  stamp.getSize();
38459         var offset = stamp.getBox();
38460         // get the columns that this stamp affects
38461         var firstX = this.isOriginLeft ? offset.x : offset.right;
38462         var lastX = firstX + stampSize.width;
38463         var firstCol = Math.floor( firstX / this.columnWidth );
38464         firstCol = Math.max( 0, firstCol );
38465         
38466         var lastCol = Math.floor( lastX / this.columnWidth );
38467         // lastCol should not go over if multiple of columnWidth #425
38468         lastCol -= lastX % this.columnWidth ? 0 : 1;
38469         lastCol = Math.min( this.cols - 1, lastCol );
38470         
38471         // set colYs to bottom of the stamp
38472         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
38473             stampSize.height;
38474             
38475         for ( var i = firstCol; i <= lastCol; i++ ) {
38476           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
38477         }
38478     },
38479     */
38480     
38481     _getContainerSize : function()
38482     {
38483         this.maxY = Math.max.apply( Math, this.colYs );
38484         var size = {
38485             height: this.maxY
38486         };
38487       
38488         if ( this.isFitWidth ) {
38489             size.width = this._getContainerFitWidth();
38490         }
38491       
38492         return size;
38493     },
38494     
38495     _getContainerFitWidth : function()
38496     {
38497         var unusedCols = 0;
38498         // count unused columns
38499         var i = this.cols;
38500         while ( --i ) {
38501           if ( this.colYs[i] !== 0 ) {
38502             break;
38503           }
38504           unusedCols++;
38505         }
38506         // fit container to columns that have been used
38507         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
38508     },
38509     
38510     needsResizeLayout : function()
38511     {
38512         var previousWidth = this.containerWidth;
38513         this.getContainerWidth();
38514         return previousWidth !== this.containerWidth;
38515     }
38516  
38517 });
38518
38519  
38520
38521  /*
38522  * - LGPL
38523  *
38524  * element
38525  * 
38526  */
38527
38528 /**
38529  * @class Roo.bootstrap.MasonryBrick
38530  * @extends Roo.bootstrap.Component
38531  * Bootstrap MasonryBrick class
38532  * 
38533  * @constructor
38534  * Create a new MasonryBrick
38535  * @param {Object} config The config object
38536  */
38537
38538 Roo.bootstrap.MasonryBrick = function(config){
38539     
38540     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
38541     
38542     Roo.bootstrap.MasonryBrick.register(this);
38543     
38544     this.addEvents({
38545         // raw events
38546         /**
38547          * @event click
38548          * When a MasonryBrick is clcik
38549          * @param {Roo.bootstrap.MasonryBrick} this
38550          * @param {Roo.EventObject} e
38551          */
38552         "click" : true
38553     });
38554 };
38555
38556 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
38557     
38558     /**
38559      * @cfg {String} title
38560      */   
38561     title : '',
38562     /**
38563      * @cfg {String} html
38564      */   
38565     html : '',
38566     /**
38567      * @cfg {String} bgimage
38568      */   
38569     bgimage : '',
38570     /**
38571      * @cfg {String} videourl
38572      */   
38573     videourl : '',
38574     /**
38575      * @cfg {String} cls
38576      */   
38577     cls : '',
38578     /**
38579      * @cfg {String} href
38580      */   
38581     href : '',
38582     /**
38583      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
38584      */   
38585     size : 'xs',
38586     
38587     /**
38588      * @cfg {String} placetitle (center|bottom)
38589      */   
38590     placetitle : '',
38591     
38592     /**
38593      * @cfg {Boolean} isFitContainer defalut true
38594      */   
38595     isFitContainer : true, 
38596     
38597     /**
38598      * @cfg {Boolean} preventDefault defalut false
38599      */   
38600     preventDefault : false, 
38601     
38602     /**
38603      * @cfg {Boolean} inverse defalut false
38604      */   
38605     maskInverse : false, 
38606     
38607     getAutoCreate : function()
38608     {
38609         if(!this.isFitContainer){
38610             return this.getSplitAutoCreate();
38611         }
38612         
38613         var cls = 'masonry-brick masonry-brick-full';
38614         
38615         if(this.href.length){
38616             cls += ' masonry-brick-link';
38617         }
38618         
38619         if(this.bgimage.length){
38620             cls += ' masonry-brick-image';
38621         }
38622         
38623         if(this.maskInverse){
38624             cls += ' mask-inverse';
38625         }
38626         
38627         if(!this.html.length && !this.maskInverse && !this.videourl.length){
38628             cls += ' enable-mask';
38629         }
38630         
38631         if(this.size){
38632             cls += ' masonry-' + this.size + '-brick';
38633         }
38634         
38635         if(this.placetitle.length){
38636             
38637             switch (this.placetitle) {
38638                 case 'center' :
38639                     cls += ' masonry-center-title';
38640                     break;
38641                 case 'bottom' :
38642                     cls += ' masonry-bottom-title';
38643                     break;
38644                 default:
38645                     break;
38646             }
38647             
38648         } else {
38649             if(!this.html.length && !this.bgimage.length){
38650                 cls += ' masonry-center-title';
38651             }
38652
38653             if(!this.html.length && this.bgimage.length){
38654                 cls += ' masonry-bottom-title';
38655             }
38656         }
38657         
38658         if(this.cls){
38659             cls += ' ' + this.cls;
38660         }
38661         
38662         var cfg = {
38663             tag: (this.href.length) ? 'a' : 'div',
38664             cls: cls,
38665             cn: [
38666                 {
38667                     tag: 'div',
38668                     cls: 'masonry-brick-mask'
38669                 },
38670                 {
38671                     tag: 'div',
38672                     cls: 'masonry-brick-paragraph',
38673                     cn: []
38674                 }
38675             ]
38676         };
38677         
38678         if(this.href.length){
38679             cfg.href = this.href;
38680         }
38681         
38682         var cn = cfg.cn[1].cn;
38683         
38684         if(this.title.length){
38685             cn.push({
38686                 tag: 'h4',
38687                 cls: 'masonry-brick-title',
38688                 html: this.title
38689             });
38690         }
38691         
38692         if(this.html.length){
38693             cn.push({
38694                 tag: 'p',
38695                 cls: 'masonry-brick-text',
38696                 html: this.html
38697             });
38698         }
38699         
38700         if (!this.title.length && !this.html.length) {
38701             cfg.cn[1].cls += ' hide';
38702         }
38703         
38704         if(this.bgimage.length){
38705             cfg.cn.push({
38706                 tag: 'img',
38707                 cls: 'masonry-brick-image-view',
38708                 src: this.bgimage
38709             });
38710         }
38711         
38712         if(this.videourl.length){
38713             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
38714             // youtube support only?
38715             cfg.cn.push({
38716                 tag: 'iframe',
38717                 cls: 'masonry-brick-image-view',
38718                 src: vurl,
38719                 frameborder : 0,
38720                 allowfullscreen : true
38721             });
38722         }
38723         
38724         return cfg;
38725         
38726     },
38727     
38728     getSplitAutoCreate : function()
38729     {
38730         var cls = 'masonry-brick masonry-brick-split';
38731         
38732         if(this.href.length){
38733             cls += ' masonry-brick-link';
38734         }
38735         
38736         if(this.bgimage.length){
38737             cls += ' masonry-brick-image';
38738         }
38739         
38740         if(this.size){
38741             cls += ' masonry-' + this.size + '-brick';
38742         }
38743         
38744         switch (this.placetitle) {
38745             case 'center' :
38746                 cls += ' masonry-center-title';
38747                 break;
38748             case 'bottom' :
38749                 cls += ' masonry-bottom-title';
38750                 break;
38751             default:
38752                 if(!this.bgimage.length){
38753                     cls += ' masonry-center-title';
38754                 }
38755
38756                 if(this.bgimage.length){
38757                     cls += ' masonry-bottom-title';
38758                 }
38759                 break;
38760         }
38761         
38762         if(this.cls){
38763             cls += ' ' + this.cls;
38764         }
38765         
38766         var cfg = {
38767             tag: (this.href.length) ? 'a' : 'div',
38768             cls: cls,
38769             cn: [
38770                 {
38771                     tag: 'div',
38772                     cls: 'masonry-brick-split-head',
38773                     cn: [
38774                         {
38775                             tag: 'div',
38776                             cls: 'masonry-brick-paragraph',
38777                             cn: []
38778                         }
38779                     ]
38780                 },
38781                 {
38782                     tag: 'div',
38783                     cls: 'masonry-brick-split-body',
38784                     cn: []
38785                 }
38786             ]
38787         };
38788         
38789         if(this.href.length){
38790             cfg.href = this.href;
38791         }
38792         
38793         if(this.title.length){
38794             cfg.cn[0].cn[0].cn.push({
38795                 tag: 'h4',
38796                 cls: 'masonry-brick-title',
38797                 html: this.title
38798             });
38799         }
38800         
38801         if(this.html.length){
38802             cfg.cn[1].cn.push({
38803                 tag: 'p',
38804                 cls: 'masonry-brick-text',
38805                 html: this.html
38806             });
38807         }
38808
38809         if(this.bgimage.length){
38810             cfg.cn[0].cn.push({
38811                 tag: 'img',
38812                 cls: 'masonry-brick-image-view',
38813                 src: this.bgimage
38814             });
38815         }
38816         
38817         if(this.videourl.length){
38818             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
38819             // youtube support only?
38820             cfg.cn[0].cn.cn.push({
38821                 tag: 'iframe',
38822                 cls: 'masonry-brick-image-view',
38823                 src: vurl,
38824                 frameborder : 0,
38825                 allowfullscreen : true
38826             });
38827         }
38828         
38829         return cfg;
38830     },
38831     
38832     initEvents: function() 
38833     {
38834         switch (this.size) {
38835             case 'xs' :
38836                 this.x = 1;
38837                 this.y = 1;
38838                 break;
38839             case 'sm' :
38840                 this.x = 2;
38841                 this.y = 2;
38842                 break;
38843             case 'md' :
38844             case 'md-left' :
38845             case 'md-right' :
38846                 this.x = 3;
38847                 this.y = 3;
38848                 break;
38849             case 'tall' :
38850                 this.x = 2;
38851                 this.y = 3;
38852                 break;
38853             case 'wide' :
38854                 this.x = 3;
38855                 this.y = 2;
38856                 break;
38857             case 'wide-thin' :
38858                 this.x = 3;
38859                 this.y = 1;
38860                 break;
38861                         
38862             default :
38863                 break;
38864         }
38865         
38866         if(Roo.isTouch){
38867             this.el.on('touchstart', this.onTouchStart, this);
38868             this.el.on('touchmove', this.onTouchMove, this);
38869             this.el.on('touchend', this.onTouchEnd, this);
38870             this.el.on('contextmenu', this.onContextMenu, this);
38871         } else {
38872             this.el.on('mouseenter'  ,this.enter, this);
38873             this.el.on('mouseleave', this.leave, this);
38874             this.el.on('click', this.onClick, this);
38875         }
38876         
38877         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
38878             this.parent().bricks.push(this);   
38879         }
38880         
38881     },
38882     
38883     onClick: function(e, el)
38884     {
38885         var time = this.endTimer - this.startTimer;
38886         // Roo.log(e.preventDefault());
38887         if(Roo.isTouch){
38888             if(time > 1000){
38889                 e.preventDefault();
38890                 return;
38891             }
38892         }
38893         
38894         if(!this.preventDefault){
38895             return;
38896         }
38897         
38898         e.preventDefault();
38899         
38900         if (this.activeClass != '') {
38901             this.selectBrick();
38902         }
38903         
38904         this.fireEvent('click', this, e);
38905     },
38906     
38907     enter: function(e, el)
38908     {
38909         e.preventDefault();
38910         
38911         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
38912             return;
38913         }
38914         
38915         if(this.bgimage.length && this.html.length){
38916             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
38917         }
38918     },
38919     
38920     leave: function(e, el)
38921     {
38922         e.preventDefault();
38923         
38924         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
38925             return;
38926         }
38927         
38928         if(this.bgimage.length && this.html.length){
38929             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
38930         }
38931     },
38932     
38933     onTouchStart: function(e, el)
38934     {
38935 //        e.preventDefault();
38936         
38937         this.touchmoved = false;
38938         
38939         if(!this.isFitContainer){
38940             return;
38941         }
38942         
38943         if(!this.bgimage.length || !this.html.length){
38944             return;
38945         }
38946         
38947         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
38948         
38949         this.timer = new Date().getTime();
38950         
38951     },
38952     
38953     onTouchMove: function(e, el)
38954     {
38955         this.touchmoved = true;
38956     },
38957     
38958     onContextMenu : function(e,el)
38959     {
38960         e.preventDefault();
38961         e.stopPropagation();
38962         return false;
38963     },
38964     
38965     onTouchEnd: function(e, el)
38966     {
38967 //        e.preventDefault();
38968         
38969         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
38970         
38971             this.leave(e,el);
38972             
38973             return;
38974         }
38975         
38976         if(!this.bgimage.length || !this.html.length){
38977             
38978             if(this.href.length){
38979                 window.location.href = this.href;
38980             }
38981             
38982             return;
38983         }
38984         
38985         if(!this.isFitContainer){
38986             return;
38987         }
38988         
38989         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
38990         
38991         window.location.href = this.href;
38992     },
38993     
38994     //selection on single brick only
38995     selectBrick : function() {
38996         
38997         if (!this.parentId) {
38998             return;
38999         }
39000         
39001         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
39002         var index = m.selectedBrick.indexOf(this.id);
39003         
39004         if ( index > -1) {
39005             m.selectedBrick.splice(index,1);
39006             this.el.removeClass(this.activeClass);
39007             return;
39008         }
39009         
39010         for(var i = 0; i < m.selectedBrick.length; i++) {
39011             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
39012             b.el.removeClass(b.activeClass);
39013         }
39014         
39015         m.selectedBrick = [];
39016         
39017         m.selectedBrick.push(this.id);
39018         this.el.addClass(this.activeClass);
39019         return;
39020     },
39021     
39022     isSelected : function(){
39023         return this.el.hasClass(this.activeClass);
39024         
39025     }
39026 });
39027
39028 Roo.apply(Roo.bootstrap.MasonryBrick, {
39029     
39030     //groups: {},
39031     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
39032      /**
39033     * register a Masonry Brick
39034     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39035     */
39036     
39037     register : function(brick)
39038     {
39039         //this.groups[brick.id] = brick;
39040         this.groups.add(brick.id, brick);
39041     },
39042     /**
39043     * fetch a  masonry brick based on the masonry brick ID
39044     * @param {string} the masonry brick to add
39045     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
39046     */
39047     
39048     get: function(brick_id) 
39049     {
39050         // if (typeof(this.groups[brick_id]) == 'undefined') {
39051         //     return false;
39052         // }
39053         // return this.groups[brick_id] ;
39054         
39055         if(this.groups.key(brick_id)) {
39056             return this.groups.key(brick_id);
39057         }
39058         
39059         return false;
39060     }
39061     
39062     
39063     
39064 });
39065
39066  /*
39067  * - LGPL
39068  *
39069  * element
39070  * 
39071  */
39072
39073 /**
39074  * @class Roo.bootstrap.Brick
39075  * @extends Roo.bootstrap.Component
39076  * Bootstrap Brick class
39077  * 
39078  * @constructor
39079  * Create a new Brick
39080  * @param {Object} config The config object
39081  */
39082
39083 Roo.bootstrap.Brick = function(config){
39084     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
39085     
39086     this.addEvents({
39087         // raw events
39088         /**
39089          * @event click
39090          * When a Brick is click
39091          * @param {Roo.bootstrap.Brick} this
39092          * @param {Roo.EventObject} e
39093          */
39094         "click" : true
39095     });
39096 };
39097
39098 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
39099     
39100     /**
39101      * @cfg {String} title
39102      */   
39103     title : '',
39104     /**
39105      * @cfg {String} html
39106      */   
39107     html : '',
39108     /**
39109      * @cfg {String} bgimage
39110      */   
39111     bgimage : '',
39112     /**
39113      * @cfg {String} cls
39114      */   
39115     cls : '',
39116     /**
39117      * @cfg {String} href
39118      */   
39119     href : '',
39120     /**
39121      * @cfg {String} video
39122      */   
39123     video : '',
39124     /**
39125      * @cfg {Boolean} square
39126      */   
39127     square : true,
39128     
39129     getAutoCreate : function()
39130     {
39131         var cls = 'roo-brick';
39132         
39133         if(this.href.length){
39134             cls += ' roo-brick-link';
39135         }
39136         
39137         if(this.bgimage.length){
39138             cls += ' roo-brick-image';
39139         }
39140         
39141         if(!this.html.length && !this.bgimage.length){
39142             cls += ' roo-brick-center-title';
39143         }
39144         
39145         if(!this.html.length && this.bgimage.length){
39146             cls += ' roo-brick-bottom-title';
39147         }
39148         
39149         if(this.cls){
39150             cls += ' ' + this.cls;
39151         }
39152         
39153         var cfg = {
39154             tag: (this.href.length) ? 'a' : 'div',
39155             cls: cls,
39156             cn: [
39157                 {
39158                     tag: 'div',
39159                     cls: 'roo-brick-paragraph',
39160                     cn: []
39161                 }
39162             ]
39163         };
39164         
39165         if(this.href.length){
39166             cfg.href = this.href;
39167         }
39168         
39169         var cn = cfg.cn[0].cn;
39170         
39171         if(this.title.length){
39172             cn.push({
39173                 tag: 'h4',
39174                 cls: 'roo-brick-title',
39175                 html: this.title
39176             });
39177         }
39178         
39179         if(this.html.length){
39180             cn.push({
39181                 tag: 'p',
39182                 cls: 'roo-brick-text',
39183                 html: this.html
39184             });
39185         } else {
39186             cn.cls += ' hide';
39187         }
39188         
39189         if(this.bgimage.length){
39190             cfg.cn.push({
39191                 tag: 'img',
39192                 cls: 'roo-brick-image-view',
39193                 src: this.bgimage
39194             });
39195         }
39196         
39197         return cfg;
39198     },
39199     
39200     initEvents: function() 
39201     {
39202         if(this.title.length || this.html.length){
39203             this.el.on('mouseenter'  ,this.enter, this);
39204             this.el.on('mouseleave', this.leave, this);
39205         }
39206         
39207         Roo.EventManager.onWindowResize(this.resize, this); 
39208         
39209         if(this.bgimage.length){
39210             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
39211             this.imageEl.on('load', this.onImageLoad, this);
39212             return;
39213         }
39214         
39215         this.resize();
39216     },
39217     
39218     onImageLoad : function()
39219     {
39220         this.resize();
39221     },
39222     
39223     resize : function()
39224     {
39225         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
39226         
39227         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
39228         
39229         if(this.bgimage.length){
39230             var image = this.el.select('.roo-brick-image-view', true).first();
39231             
39232             image.setWidth(paragraph.getWidth());
39233             
39234             if(this.square){
39235                 image.setHeight(paragraph.getWidth());
39236             }
39237             
39238             this.el.setHeight(image.getHeight());
39239             paragraph.setHeight(image.getHeight());
39240             
39241         }
39242         
39243     },
39244     
39245     enter: function(e, el)
39246     {
39247         e.preventDefault();
39248         
39249         if(this.bgimage.length){
39250             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
39251             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
39252         }
39253     },
39254     
39255     leave: function(e, el)
39256     {
39257         e.preventDefault();
39258         
39259         if(this.bgimage.length){
39260             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
39261             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
39262         }
39263     }
39264     
39265 });
39266
39267  
39268
39269  /*
39270  * - LGPL
39271  *
39272  * Number field 
39273  */
39274
39275 /**
39276  * @class Roo.bootstrap.form.NumberField
39277  * @extends Roo.bootstrap.form.Input
39278  * Bootstrap NumberField class
39279  * 
39280  * 
39281  * 
39282  * 
39283  * @constructor
39284  * Create a new NumberField
39285  * @param {Object} config The config object
39286  */
39287
39288 Roo.bootstrap.form.NumberField = function(config){
39289     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
39290 };
39291
39292 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
39293     
39294     /**
39295      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39296      */
39297     allowDecimals : true,
39298     /**
39299      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39300      */
39301     decimalSeparator : ".",
39302     /**
39303      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39304      */
39305     decimalPrecision : 2,
39306     /**
39307      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39308      */
39309     allowNegative : true,
39310     
39311     /**
39312      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
39313      */
39314     allowZero: true,
39315     /**
39316      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39317      */
39318     minValue : Number.NEGATIVE_INFINITY,
39319     /**
39320      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39321      */
39322     maxValue : Number.MAX_VALUE,
39323     /**
39324      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39325      */
39326     minText : "The minimum value for this field is {0}",
39327     /**
39328      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39329      */
39330     maxText : "The maximum value for this field is {0}",
39331     /**
39332      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39333      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39334      */
39335     nanText : "{0} is not a valid number",
39336     /**
39337      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
39338      */
39339     thousandsDelimiter : false,
39340     /**
39341      * @cfg {String} valueAlign alignment of value
39342      */
39343     valueAlign : "left",
39344
39345     getAutoCreate : function()
39346     {
39347         var hiddenInput = {
39348             tag: 'input',
39349             type: 'hidden',
39350             id: Roo.id(),
39351             cls: 'hidden-number-input'
39352         };
39353         
39354         if (this.name) {
39355             hiddenInput.name = this.name;
39356         }
39357         
39358         this.name = '';
39359         
39360         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
39361         
39362         this.name = hiddenInput.name;
39363         
39364         if(cfg.cn.length > 0) {
39365             cfg.cn.push(hiddenInput);
39366         }
39367         
39368         return cfg;
39369     },
39370
39371     // private
39372     initEvents : function()
39373     {   
39374         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
39375         
39376         var allowed = "0123456789";
39377         
39378         if(this.allowDecimals){
39379             allowed += this.decimalSeparator;
39380         }
39381         
39382         if(this.allowNegative){
39383             allowed += "-";
39384         }
39385         
39386         if(this.thousandsDelimiter) {
39387             allowed += ",";
39388         }
39389         
39390         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39391         
39392         var keyPress = function(e){
39393             
39394             var k = e.getKey();
39395             
39396             var c = e.getCharCode();
39397             
39398             if(
39399                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39400                     allowed.indexOf(String.fromCharCode(c)) === -1
39401             ){
39402                 e.stopEvent();
39403                 return;
39404             }
39405             
39406             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39407                 return;
39408             }
39409             
39410             if(allowed.indexOf(String.fromCharCode(c)) === -1){
39411                 e.stopEvent();
39412             }
39413         };
39414         
39415         this.el.on("keypress", keyPress, this);
39416     },
39417     
39418     validateValue : function(value)
39419     {
39420         
39421         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
39422             return false;
39423         }
39424         
39425         var num = this.parseValue(value);
39426         
39427         if(isNaN(num)){
39428             this.markInvalid(String.format(this.nanText, value));
39429             return false;
39430         }
39431         
39432         if(num < this.minValue){
39433             this.markInvalid(String.format(this.minText, this.minValue));
39434             return false;
39435         }
39436         
39437         if(num > this.maxValue){
39438             this.markInvalid(String.format(this.maxText, this.maxValue));
39439             return false;
39440         }
39441         
39442         return true;
39443     },
39444
39445     getValue : function()
39446     {
39447         var v = this.hiddenEl().getValue();
39448         
39449         return this.fixPrecision(this.parseValue(v));
39450     },
39451
39452     parseValue : function(value)
39453     {
39454         if(this.thousandsDelimiter) {
39455             value += "";
39456             r = new RegExp(",", "g");
39457             value = value.replace(r, "");
39458         }
39459         
39460         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
39461         return isNaN(value) ? '' : value;
39462     },
39463
39464     fixPrecision : function(value)
39465     {
39466         if(this.thousandsDelimiter) {
39467             value += "";
39468             r = new RegExp(",", "g");
39469             value = value.replace(r, "");
39470         }
39471         
39472         var nan = isNaN(value);
39473         
39474         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
39475             return nan ? '' : value;
39476         }
39477         return parseFloat(value).toFixed(this.decimalPrecision);
39478     },
39479
39480     setValue : function(v)
39481     {
39482         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
39483         
39484         this.value = v;
39485         
39486         if(this.rendered){
39487             
39488             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39489             
39490             this.inputEl().dom.value = (v == '') ? '' :
39491                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
39492             
39493             if(!this.allowZero && v === '0') {
39494                 this.hiddenEl().dom.value = '';
39495                 this.inputEl().dom.value = '';
39496             }
39497             
39498             this.validate();
39499         }
39500     },
39501
39502     decimalPrecisionFcn : function(v)
39503     {
39504         return Math.floor(v);
39505     },
39506
39507     beforeBlur : function()
39508     {
39509         var v = this.parseValue(this.getRawValue());
39510         
39511         if(v || v === 0 || v === ''){
39512             this.setValue(v);
39513         }
39514     },
39515     
39516     hiddenEl : function()
39517     {
39518         return this.el.select('input.hidden-number-input',true).first();
39519     }
39520     
39521 });
39522
39523  
39524
39525 /*
39526 * Licence: LGPL
39527 */
39528
39529 /**
39530  * @class Roo.bootstrap.DocumentSlider
39531  * @extends Roo.bootstrap.Component
39532  * Bootstrap DocumentSlider class
39533  * 
39534  * @constructor
39535  * Create a new DocumentViewer
39536  * @param {Object} config The config object
39537  */
39538
39539 Roo.bootstrap.DocumentSlider = function(config){
39540     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
39541     
39542     this.files = [];
39543     
39544     this.addEvents({
39545         /**
39546          * @event initial
39547          * Fire after initEvent
39548          * @param {Roo.bootstrap.DocumentSlider} this
39549          */
39550         "initial" : true,
39551         /**
39552          * @event update
39553          * Fire after update
39554          * @param {Roo.bootstrap.DocumentSlider} this
39555          */
39556         "update" : true,
39557         /**
39558          * @event click
39559          * Fire after click
39560          * @param {Roo.bootstrap.DocumentSlider} this
39561          */
39562         "click" : true
39563     });
39564 };
39565
39566 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
39567     
39568     files : false,
39569     
39570     indicator : 0,
39571     
39572     getAutoCreate : function()
39573     {
39574         var cfg = {
39575             tag : 'div',
39576             cls : 'roo-document-slider',
39577             cn : [
39578                 {
39579                     tag : 'div',
39580                     cls : 'roo-document-slider-header',
39581                     cn : [
39582                         {
39583                             tag : 'div',
39584                             cls : 'roo-document-slider-header-title'
39585                         }
39586                     ]
39587                 },
39588                 {
39589                     tag : 'div',
39590                     cls : 'roo-document-slider-body',
39591                     cn : [
39592                         {
39593                             tag : 'div',
39594                             cls : 'roo-document-slider-prev',
39595                             cn : [
39596                                 {
39597                                     tag : 'i',
39598                                     cls : 'fa fa-chevron-left'
39599                                 }
39600                             ]
39601                         },
39602                         {
39603                             tag : 'div',
39604                             cls : 'roo-document-slider-thumb',
39605                             cn : [
39606                                 {
39607                                     tag : 'img',
39608                                     cls : 'roo-document-slider-image'
39609                                 }
39610                             ]
39611                         },
39612                         {
39613                             tag : 'div',
39614                             cls : 'roo-document-slider-next',
39615                             cn : [
39616                                 {
39617                                     tag : 'i',
39618                                     cls : 'fa fa-chevron-right'
39619                                 }
39620                             ]
39621                         }
39622                     ]
39623                 }
39624             ]
39625         };
39626         
39627         return cfg;
39628     },
39629     
39630     initEvents : function()
39631     {
39632         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
39633         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
39634         
39635         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
39636         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
39637         
39638         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
39639         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
39640         
39641         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
39642         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
39643         
39644         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
39645         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
39646         
39647         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
39648         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39649         
39650         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
39651         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39652         
39653         this.thumbEl.on('click', this.onClick, this);
39654         
39655         this.prevIndicator.on('click', this.prev, this);
39656         
39657         this.nextIndicator.on('click', this.next, this);
39658         
39659     },
39660     
39661     initial : function()
39662     {
39663         if(this.files.length){
39664             this.indicator = 1;
39665             this.update()
39666         }
39667         
39668         this.fireEvent('initial', this);
39669     },
39670     
39671     update : function()
39672     {
39673         this.imageEl.attr('src', this.files[this.indicator - 1]);
39674         
39675         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
39676         
39677         this.prevIndicator.show();
39678         
39679         if(this.indicator == 1){
39680             this.prevIndicator.hide();
39681         }
39682         
39683         this.nextIndicator.show();
39684         
39685         if(this.indicator == this.files.length){
39686             this.nextIndicator.hide();
39687         }
39688         
39689         this.thumbEl.scrollTo('top');
39690         
39691         this.fireEvent('update', this);
39692     },
39693     
39694     onClick : function(e)
39695     {
39696         e.preventDefault();
39697         
39698         this.fireEvent('click', this);
39699     },
39700     
39701     prev : function(e)
39702     {
39703         e.preventDefault();
39704         
39705         this.indicator = Math.max(1, this.indicator - 1);
39706         
39707         this.update();
39708     },
39709     
39710     next : function(e)
39711     {
39712         e.preventDefault();
39713         
39714         this.indicator = Math.min(this.files.length, this.indicator + 1);
39715         
39716         this.update();
39717     }
39718 });
39719 /*
39720  * - LGPL
39721  *
39722  * RadioSet
39723  *
39724  *
39725  */
39726
39727 /**
39728  * @class Roo.bootstrap.form.RadioSet
39729  * @extends Roo.bootstrap.form.Input
39730  * @children Roo.bootstrap.form.Radio
39731  * Bootstrap RadioSet class
39732  * @cfg {String} indicatorpos (left|right) default left
39733  * @cfg {Boolean} inline (true|false) inline the element (default true)
39734  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
39735  * @constructor
39736  * Create a new RadioSet
39737  * @param {Object} config The config object
39738  */
39739
39740 Roo.bootstrap.form.RadioSet = function(config){
39741     
39742     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
39743     
39744     this.radioes = [];
39745     
39746     Roo.bootstrap.form.RadioSet.register(this);
39747     
39748     this.addEvents({
39749         /**
39750         * @event check
39751         * Fires when the element is checked or unchecked.
39752         * @param {Roo.bootstrap.form.RadioSet} this This radio
39753         * @param {Roo.bootstrap.form.Radio} item The checked item
39754         */
39755        check : true,
39756        /**
39757         * @event click
39758         * Fires when the element is click.
39759         * @param {Roo.bootstrap.form.RadioSet} this This radio set
39760         * @param {Roo.bootstrap.form.Radio} item The checked item
39761         * @param {Roo.EventObject} e The event object
39762         */
39763        click : true
39764     });
39765     
39766 };
39767
39768 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
39769
39770     radioes : false,
39771     
39772     inline : true,
39773     
39774     weight : '',
39775     
39776     indicatorpos : 'left',
39777     
39778     getAutoCreate : function()
39779     {
39780         var label = {
39781             tag : 'label',
39782             cls : 'roo-radio-set-label',
39783             cn : [
39784                 {
39785                     tag : 'span',
39786                     html : this.fieldLabel
39787                 }
39788             ]
39789         };
39790         if (Roo.bootstrap.version == 3) {
39791             
39792             
39793             if(this.indicatorpos == 'left'){
39794                 label.cn.unshift({
39795                     tag : 'i',
39796                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
39797                     tooltip : 'This field is required'
39798                 });
39799             } else {
39800                 label.cn.push({
39801                     tag : 'i',
39802                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
39803                     tooltip : 'This field is required'
39804                 });
39805             }
39806         }
39807         var items = {
39808             tag : 'div',
39809             cls : 'roo-radio-set-items'
39810         };
39811         
39812         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
39813         
39814         if (align === 'left' && this.fieldLabel.length) {
39815             
39816             items = {
39817                 cls : "roo-radio-set-right", 
39818                 cn: [
39819                     items
39820                 ]
39821             };
39822             
39823             if(this.labelWidth > 12){
39824                 label.style = "width: " + this.labelWidth + 'px';
39825             }
39826             
39827             if(this.labelWidth < 13 && this.labelmd == 0){
39828                 this.labelmd = this.labelWidth;
39829             }
39830             
39831             if(this.labellg > 0){
39832                 label.cls += ' col-lg-' + this.labellg;
39833                 items.cls += ' col-lg-' + (12 - this.labellg);
39834             }
39835             
39836             if(this.labelmd > 0){
39837                 label.cls += ' col-md-' + this.labelmd;
39838                 items.cls += ' col-md-' + (12 - this.labelmd);
39839             }
39840             
39841             if(this.labelsm > 0){
39842                 label.cls += ' col-sm-' + this.labelsm;
39843                 items.cls += ' col-sm-' + (12 - this.labelsm);
39844             }
39845             
39846             if(this.labelxs > 0){
39847                 label.cls += ' col-xs-' + this.labelxs;
39848                 items.cls += ' col-xs-' + (12 - this.labelxs);
39849             }
39850         }
39851         
39852         var cfg = {
39853             tag : 'div',
39854             cls : 'roo-radio-set',
39855             cn : [
39856                 {
39857                     tag : 'input',
39858                     cls : 'roo-radio-set-input',
39859                     type : 'hidden',
39860                     name : this.name,
39861                     value : this.value ? this.value :  ''
39862                 },
39863                 label,
39864                 items
39865             ]
39866         };
39867         
39868         if(this.weight.length){
39869             cfg.cls += ' roo-radio-' + this.weight;
39870         }
39871         
39872         if(this.inline) {
39873             cfg.cls += ' roo-radio-set-inline';
39874         }
39875         
39876         var settings=this;
39877         ['xs','sm','md','lg'].map(function(size){
39878             if (settings[size]) {
39879                 cfg.cls += ' col-' + size + '-' + settings[size];
39880             }
39881         });
39882         
39883         return cfg;
39884         
39885     },
39886
39887     initEvents : function()
39888     {
39889         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
39890         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
39891         
39892         if(!this.fieldLabel.length){
39893             this.labelEl.hide();
39894         }
39895         
39896         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
39897         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
39898         
39899         this.indicator = this.indicatorEl();
39900         
39901         if(this.indicator){
39902             this.indicator.addClass('invisible');
39903         }
39904         
39905         this.originalValue = this.getValue();
39906         
39907     },
39908     
39909     inputEl: function ()
39910     {
39911         return this.el.select('.roo-radio-set-input', true).first();
39912     },
39913     
39914     getChildContainer : function()
39915     {
39916         return this.itemsEl;
39917     },
39918     
39919     register : function(item)
39920     {
39921         this.radioes.push(item);
39922         
39923     },
39924     
39925     validate : function()
39926     {   
39927         if(this.getVisibilityEl().hasClass('hidden')){
39928             return true;
39929         }
39930         
39931         var valid = false;
39932         
39933         Roo.each(this.radioes, function(i){
39934             if(!i.checked){
39935                 return;
39936             }
39937             
39938             valid = true;
39939             return false;
39940         });
39941         
39942         if(this.allowBlank) {
39943             return true;
39944         }
39945         
39946         if(this.disabled || valid){
39947             this.markValid();
39948             return true;
39949         }
39950         
39951         this.markInvalid();
39952         return false;
39953         
39954     },
39955     
39956     markValid : function()
39957     {
39958         if(this.labelEl.isVisible(true) && this.indicatorEl()){
39959             this.indicatorEl().removeClass('visible');
39960             this.indicatorEl().addClass('invisible');
39961         }
39962         
39963         
39964         if (Roo.bootstrap.version == 3) {
39965             this.el.removeClass([this.invalidClass, this.validClass]);
39966             this.el.addClass(this.validClass);
39967         } else {
39968             this.el.removeClass(['is-invalid','is-valid']);
39969             this.el.addClass(['is-valid']);
39970         }
39971         this.fireEvent('valid', this);
39972     },
39973     
39974     markInvalid : function(msg)
39975     {
39976         if(this.allowBlank || this.disabled){
39977             return;
39978         }
39979         
39980         if(this.labelEl.isVisible(true) && this.indicatorEl()){
39981             this.indicatorEl().removeClass('invisible');
39982             this.indicatorEl().addClass('visible');
39983         }
39984         if (Roo.bootstrap.version == 3) {
39985             this.el.removeClass([this.invalidClass, this.validClass]);
39986             this.el.addClass(this.invalidClass);
39987         } else {
39988             this.el.removeClass(['is-invalid','is-valid']);
39989             this.el.addClass(['is-invalid']);
39990         }
39991         
39992         this.fireEvent('invalid', this, msg);
39993         
39994     },
39995     
39996     setValue : function(v, suppressEvent)
39997     {   
39998         if(this.value === v){
39999             return;
40000         }
40001         
40002         this.value = v;
40003         
40004         if(this.rendered){
40005             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40006         }
40007         
40008         Roo.each(this.radioes, function(i){
40009             i.checked = false;
40010             i.el.removeClass('checked');
40011         });
40012         
40013         Roo.each(this.radioes, function(i){
40014             
40015             if(i.value === v || i.value.toString() === v.toString()){
40016                 i.checked = true;
40017                 i.el.addClass('checked');
40018                 
40019                 if(suppressEvent !== true){
40020                     this.fireEvent('check', this, i);
40021                 }
40022                 
40023                 return false;
40024             }
40025             
40026         }, this);
40027         
40028         this.validate();
40029     },
40030     
40031     clearInvalid : function(){
40032         
40033         if(!this.el || this.preventMark){
40034             return;
40035         }
40036         
40037         this.el.removeClass([this.invalidClass]);
40038         
40039         this.fireEvent('valid', this);
40040     }
40041     
40042 });
40043
40044 Roo.apply(Roo.bootstrap.form.RadioSet, {
40045     
40046     groups: {},
40047     
40048     register : function(set)
40049     {
40050         this.groups[set.name] = set;
40051     },
40052     
40053     get: function(name) 
40054     {
40055         if (typeof(this.groups[name]) == 'undefined') {
40056             return false;
40057         }
40058         
40059         return this.groups[name] ;
40060     }
40061     
40062 });
40063 /*
40064  * Based on:
40065  * Ext JS Library 1.1.1
40066  * Copyright(c) 2006-2007, Ext JS, LLC.
40067  *
40068  * Originally Released Under LGPL - original licence link has changed is not relivant.
40069  *
40070  * Fork - LGPL
40071  * <script type="text/javascript">
40072  */
40073
40074
40075 /**
40076  * @class Roo.bootstrap.SplitBar
40077  * @extends Roo.util.Observable
40078  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
40079  * <br><br>
40080  * Usage:
40081  * <pre><code>
40082 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
40083                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
40084 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
40085 split.minSize = 100;
40086 split.maxSize = 600;
40087 split.animate = true;
40088 split.on('moved', splitterMoved);
40089 </code></pre>
40090  * @constructor
40091  * Create a new SplitBar
40092  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
40093  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
40094  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40095  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
40096                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
40097                         position of the SplitBar).
40098  */
40099 Roo.bootstrap.SplitBar = function(cfg){
40100     
40101     /** @private */
40102     
40103     //{
40104     //  dragElement : elm
40105     //  resizingElement: el,
40106         // optional..
40107     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
40108     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
40109         // existingProxy ???
40110     //}
40111     
40112     this.el = Roo.get(cfg.dragElement, true);
40113     this.el.dom.unselectable = "on";
40114     /** @private */
40115     this.resizingEl = Roo.get(cfg.resizingElement, true);
40116
40117     /**
40118      * @private
40119      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40120      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
40121      * @type Number
40122      */
40123     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
40124     
40125     /**
40126      * The minimum size of the resizing element. (Defaults to 0)
40127      * @type Number
40128      */
40129     this.minSize = 0;
40130     
40131     /**
40132      * The maximum size of the resizing element. (Defaults to 2000)
40133      * @type Number
40134      */
40135     this.maxSize = 2000;
40136     
40137     /**
40138      * Whether to animate the transition to the new size
40139      * @type Boolean
40140      */
40141     this.animate = false;
40142     
40143     /**
40144      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
40145      * @type Boolean
40146      */
40147     this.useShim = false;
40148     
40149     /** @private */
40150     this.shim = null;
40151     
40152     if(!cfg.existingProxy){
40153         /** @private */
40154         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
40155     }else{
40156         this.proxy = Roo.get(cfg.existingProxy).dom;
40157     }
40158     /** @private */
40159     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
40160     
40161     /** @private */
40162     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
40163     
40164     /** @private */
40165     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
40166     
40167     /** @private */
40168     this.dragSpecs = {};
40169     
40170     /**
40171      * @private The adapter to use to positon and resize elements
40172      */
40173     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40174     this.adapter.init(this);
40175     
40176     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40177         /** @private */
40178         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
40179         this.el.addClass("roo-splitbar-h");
40180     }else{
40181         /** @private */
40182         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
40183         this.el.addClass("roo-splitbar-v");
40184     }
40185     
40186     this.addEvents({
40187         /**
40188          * @event resize
40189          * Fires when the splitter is moved (alias for {@link #event-moved})
40190          * @param {Roo.bootstrap.SplitBar} this
40191          * @param {Number} newSize the new width or height
40192          */
40193         "resize" : true,
40194         /**
40195          * @event moved
40196          * Fires when the splitter is moved
40197          * @param {Roo.bootstrap.SplitBar} this
40198          * @param {Number} newSize the new width or height
40199          */
40200         "moved" : true,
40201         /**
40202          * @event beforeresize
40203          * Fires before the splitter is dragged
40204          * @param {Roo.bootstrap.SplitBar} this
40205          */
40206         "beforeresize" : true,
40207
40208         "beforeapply" : true
40209     });
40210
40211     Roo.util.Observable.call(this);
40212 };
40213
40214 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
40215     onStartProxyDrag : function(x, y){
40216         this.fireEvent("beforeresize", this);
40217         if(!this.overlay){
40218             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
40219             o.unselectable();
40220             o.enableDisplayMode("block");
40221             // all splitbars share the same overlay
40222             Roo.bootstrap.SplitBar.prototype.overlay = o;
40223         }
40224         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
40225         this.overlay.show();
40226         Roo.get(this.proxy).setDisplayed("block");
40227         var size = this.adapter.getElementSize(this);
40228         this.activeMinSize = this.getMinimumSize();;
40229         this.activeMaxSize = this.getMaximumSize();;
40230         var c1 = size - this.activeMinSize;
40231         var c2 = Math.max(this.activeMaxSize - size, 0);
40232         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40233             this.dd.resetConstraints();
40234             this.dd.setXConstraint(
40235                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
40236                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
40237             );
40238             this.dd.setYConstraint(0, 0);
40239         }else{
40240             this.dd.resetConstraints();
40241             this.dd.setXConstraint(0, 0);
40242             this.dd.setYConstraint(
40243                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
40244                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
40245             );
40246          }
40247         this.dragSpecs.startSize = size;
40248         this.dragSpecs.startPoint = [x, y];
40249         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
40250     },
40251     
40252     /** 
40253      * @private Called after the drag operation by the DDProxy
40254      */
40255     onEndProxyDrag : function(e){
40256         Roo.get(this.proxy).setDisplayed(false);
40257         var endPoint = Roo.lib.Event.getXY(e);
40258         if(this.overlay){
40259             this.overlay.hide();
40260         }
40261         var newSize;
40262         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40263             newSize = this.dragSpecs.startSize + 
40264                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
40265                     endPoint[0] - this.dragSpecs.startPoint[0] :
40266                     this.dragSpecs.startPoint[0] - endPoint[0]
40267                 );
40268         }else{
40269             newSize = this.dragSpecs.startSize + 
40270                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
40271                     endPoint[1] - this.dragSpecs.startPoint[1] :
40272                     this.dragSpecs.startPoint[1] - endPoint[1]
40273                 );
40274         }
40275         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
40276         if(newSize != this.dragSpecs.startSize){
40277             if(this.fireEvent('beforeapply', this, newSize) !== false){
40278                 this.adapter.setElementSize(this, newSize);
40279                 this.fireEvent("moved", this, newSize);
40280                 this.fireEvent("resize", this, newSize);
40281             }
40282         }
40283     },
40284     
40285     /**
40286      * Get the adapter this SplitBar uses
40287      * @return The adapter object
40288      */
40289     getAdapter : function(){
40290         return this.adapter;
40291     },
40292     
40293     /**
40294      * Set the adapter this SplitBar uses
40295      * @param {Object} adapter A SplitBar adapter object
40296      */
40297     setAdapter : function(adapter){
40298         this.adapter = adapter;
40299         this.adapter.init(this);
40300     },
40301     
40302     /**
40303      * Gets the minimum size for the resizing element
40304      * @return {Number} The minimum size
40305      */
40306     getMinimumSize : function(){
40307         return this.minSize;
40308     },
40309     
40310     /**
40311      * Sets the minimum size for the resizing element
40312      * @param {Number} minSize The minimum size
40313      */
40314     setMinimumSize : function(minSize){
40315         this.minSize = minSize;
40316     },
40317     
40318     /**
40319      * Gets the maximum size for the resizing element
40320      * @return {Number} The maximum size
40321      */
40322     getMaximumSize : function(){
40323         return this.maxSize;
40324     },
40325     
40326     /**
40327      * Sets the maximum size for the resizing element
40328      * @param {Number} maxSize The maximum size
40329      */
40330     setMaximumSize : function(maxSize){
40331         this.maxSize = maxSize;
40332     },
40333     
40334     /**
40335      * Sets the initialize size for the resizing element
40336      * @param {Number} size The initial size
40337      */
40338     setCurrentSize : function(size){
40339         var oldAnimate = this.animate;
40340         this.animate = false;
40341         this.adapter.setElementSize(this, size);
40342         this.animate = oldAnimate;
40343     },
40344     
40345     /**
40346      * Destroy this splitbar. 
40347      * @param {Boolean} removeEl True to remove the element
40348      */
40349     destroy : function(removeEl){
40350         if(this.shim){
40351             this.shim.remove();
40352         }
40353         this.dd.unreg();
40354         this.proxy.parentNode.removeChild(this.proxy);
40355         if(removeEl){
40356             this.el.remove();
40357         }
40358     }
40359 });
40360
40361 /**
40362  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
40363  */
40364 Roo.bootstrap.SplitBar.createProxy = function(dir){
40365     var proxy = new Roo.Element(document.createElement("div"));
40366     proxy.unselectable();
40367     var cls = 'roo-splitbar-proxy';
40368     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
40369     document.body.appendChild(proxy.dom);
40370     return proxy.dom;
40371 };
40372
40373 /** 
40374  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
40375  * Default Adapter. It assumes the splitter and resizing element are not positioned
40376  * elements and only gets/sets the width of the element. Generally used for table based layouts.
40377  */
40378 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
40379 };
40380
40381 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
40382     // do nothing for now
40383     init : function(s){
40384     
40385     },
40386     /**
40387      * Called before drag operations to get the current size of the resizing element. 
40388      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40389      */
40390      getElementSize : function(s){
40391         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40392             return s.resizingEl.getWidth();
40393         }else{
40394             return s.resizingEl.getHeight();
40395         }
40396     },
40397     
40398     /**
40399      * Called after drag operations to set the size of the resizing element.
40400      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40401      * @param {Number} newSize The new size to set
40402      * @param {Function} onComplete A function to be invoked when resizing is complete
40403      */
40404     setElementSize : function(s, newSize, onComplete){
40405         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40406             if(!s.animate){
40407                 s.resizingEl.setWidth(newSize);
40408                 if(onComplete){
40409                     onComplete(s, newSize);
40410                 }
40411             }else{
40412                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
40413             }
40414         }else{
40415             
40416             if(!s.animate){
40417                 s.resizingEl.setHeight(newSize);
40418                 if(onComplete){
40419                     onComplete(s, newSize);
40420                 }
40421             }else{
40422                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
40423             }
40424         }
40425     }
40426 };
40427
40428 /** 
40429  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
40430  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
40431  * Adapter that  moves the splitter element to align with the resized sizing element. 
40432  * Used with an absolute positioned SplitBar.
40433  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
40434  * document.body, make sure you assign an id to the body element.
40435  */
40436 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
40437     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40438     this.container = Roo.get(container);
40439 };
40440
40441 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
40442     init : function(s){
40443         this.basic.init(s);
40444     },
40445     
40446     getElementSize : function(s){
40447         return this.basic.getElementSize(s);
40448     },
40449     
40450     setElementSize : function(s, newSize, onComplete){
40451         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
40452     },
40453     
40454     moveSplitter : function(s){
40455         var yes = Roo.bootstrap.SplitBar;
40456         switch(s.placement){
40457             case yes.LEFT:
40458                 s.el.setX(s.resizingEl.getRight());
40459                 break;
40460             case yes.RIGHT:
40461                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
40462                 break;
40463             case yes.TOP:
40464                 s.el.setY(s.resizingEl.getBottom());
40465                 break;
40466             case yes.BOTTOM:
40467                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
40468                 break;
40469         }
40470     }
40471 };
40472
40473 /**
40474  * Orientation constant - Create a vertical SplitBar
40475  * @static
40476  * @type Number
40477  */
40478 Roo.bootstrap.SplitBar.VERTICAL = 1;
40479
40480 /**
40481  * Orientation constant - Create a horizontal SplitBar
40482  * @static
40483  * @type Number
40484  */
40485 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
40486
40487 /**
40488  * Placement constant - The resizing element is to the left of the splitter element
40489  * @static
40490  * @type Number
40491  */
40492 Roo.bootstrap.SplitBar.LEFT = 1;
40493
40494 /**
40495  * Placement constant - The resizing element is to the right of the splitter element
40496  * @static
40497  * @type Number
40498  */
40499 Roo.bootstrap.SplitBar.RIGHT = 2;
40500
40501 /**
40502  * Placement constant - The resizing element is positioned above the splitter element
40503  * @static
40504  * @type Number
40505  */
40506 Roo.bootstrap.SplitBar.TOP = 3;
40507
40508 /**
40509  * Placement constant - The resizing element is positioned under splitter element
40510  * @static
40511  * @type Number
40512  */
40513 Roo.bootstrap.SplitBar.BOTTOM = 4;
40514 /*
40515  * Based on:
40516  * Ext JS Library 1.1.1
40517  * Copyright(c) 2006-2007, Ext JS, LLC.
40518  *
40519  * Originally Released Under LGPL - original licence link has changed is not relivant.
40520  *
40521  * Fork - LGPL
40522  * <script type="text/javascript">
40523  */
40524
40525 /**
40526  * @class Roo.bootstrap.layout.Manager
40527  * @extends Roo.bootstrap.Component
40528  * @abstract
40529  * Base class for layout managers.
40530  */
40531 Roo.bootstrap.layout.Manager = function(config)
40532 {
40533     this.monitorWindowResize = true; // do this before we apply configuration.
40534     
40535     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
40536
40537
40538
40539
40540
40541     /** false to disable window resize monitoring @type Boolean */
40542     
40543     this.regions = {};
40544     this.addEvents({
40545         /**
40546          * @event layout
40547          * Fires when a layout is performed.
40548          * @param {Roo.LayoutManager} this
40549          */
40550         "layout" : true,
40551         /**
40552          * @event regionresized
40553          * Fires when the user resizes a region.
40554          * @param {Roo.LayoutRegion} region The resized region
40555          * @param {Number} newSize The new size (width for east/west, height for north/south)
40556          */
40557         "regionresized" : true,
40558         /**
40559          * @event regioncollapsed
40560          * Fires when a region is collapsed.
40561          * @param {Roo.LayoutRegion} region The collapsed region
40562          */
40563         "regioncollapsed" : true,
40564         /**
40565          * @event regionexpanded
40566          * Fires when a region is expanded.
40567          * @param {Roo.LayoutRegion} region The expanded region
40568          */
40569         "regionexpanded" : true
40570     });
40571     this.updating = false;
40572
40573     if (config.el) {
40574         this.el = Roo.get(config.el);
40575         this.initEvents();
40576     }
40577
40578 };
40579
40580 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
40581
40582
40583     regions : null,
40584
40585     monitorWindowResize : true,
40586
40587
40588     updating : false,
40589
40590
40591     onRender : function(ct, position)
40592     {
40593         if(!this.el){
40594             this.el = Roo.get(ct);
40595             this.initEvents();
40596         }
40597         //this.fireEvent('render',this);
40598     },
40599
40600
40601     initEvents: function()
40602     {
40603
40604
40605         // ie scrollbar fix
40606         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
40607             document.body.scroll = "no";
40608         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
40609             this.el.position('relative');
40610         }
40611         this.id = this.el.id;
40612         this.el.addClass("roo-layout-container");
40613         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
40614         if(this.el.dom != document.body ) {
40615             this.el.on('resize', this.layout,this);
40616             this.el.on('show', this.layout,this);
40617         }
40618
40619     },
40620
40621     /**
40622      * Returns true if this layout is currently being updated
40623      * @return {Boolean}
40624      */
40625     isUpdating : function(){
40626         return this.updating;
40627     },
40628
40629     /**
40630      * Suspend the LayoutManager from doing auto-layouts while
40631      * making multiple add or remove calls
40632      */
40633     beginUpdate : function(){
40634         this.updating = true;
40635     },
40636
40637     /**
40638      * Restore auto-layouts and optionally disable the manager from performing a layout
40639      * @param {Boolean} noLayout true to disable a layout update
40640      */
40641     endUpdate : function(noLayout){
40642         this.updating = false;
40643         if(!noLayout){
40644             this.layout();
40645         }
40646     },
40647
40648     layout: function(){
40649         // abstract...
40650     },
40651
40652     onRegionResized : function(region, newSize){
40653         this.fireEvent("regionresized", region, newSize);
40654         this.layout();
40655     },
40656
40657     onRegionCollapsed : function(region){
40658         this.fireEvent("regioncollapsed", region);
40659     },
40660
40661     onRegionExpanded : function(region){
40662         this.fireEvent("regionexpanded", region);
40663     },
40664
40665     /**
40666      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
40667      * performs box-model adjustments.
40668      * @return {Object} The size as an object {width: (the width), height: (the height)}
40669      */
40670     getViewSize : function()
40671     {
40672         var size;
40673         if(this.el.dom != document.body){
40674             size = this.el.getSize();
40675         }else{
40676             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
40677         }
40678         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
40679         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40680         return size;
40681     },
40682
40683     /**
40684      * Returns the Element this layout is bound to.
40685      * @return {Roo.Element}
40686      */
40687     getEl : function(){
40688         return this.el;
40689     },
40690
40691     /**
40692      * Returns the specified region.
40693      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
40694      * @return {Roo.LayoutRegion}
40695      */
40696     getRegion : function(target){
40697         return this.regions[target.toLowerCase()];
40698     },
40699
40700     onWindowResize : function(){
40701         if(this.monitorWindowResize){
40702             this.layout();
40703         }
40704     }
40705 });
40706 /*
40707  * Based on:
40708  * Ext JS Library 1.1.1
40709  * Copyright(c) 2006-2007, Ext JS, LLC.
40710  *
40711  * Originally Released Under LGPL - original licence link has changed is not relivant.
40712  *
40713  * Fork - LGPL
40714  * <script type="text/javascript">
40715  */
40716 /**
40717  * @class Roo.bootstrap.layout.Border
40718  * @extends Roo.bootstrap.layout.Manager
40719  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
40720  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
40721  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
40722  * please see: examples/bootstrap/nested.html<br><br>
40723  
40724 <b>The container the layout is rendered into can be either the body element or any other element.
40725 If it is not the body element, the container needs to either be an absolute positioned element,
40726 or you will need to add "position:relative" to the css of the container.  You will also need to specify
40727 the container size if it is not the body element.</b>
40728
40729 * @constructor
40730 * Create a new Border
40731 * @param {Object} config Configuration options
40732  */
40733 Roo.bootstrap.layout.Border = function(config){
40734     config = config || {};
40735     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
40736     
40737     
40738     
40739     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
40740         if(config[region]){
40741             config[region].region = region;
40742             this.addRegion(config[region]);
40743         }
40744     },this);
40745     
40746 };
40747
40748 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
40749
40750 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
40751     
40752         /**
40753          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
40754          */
40755         /**
40756          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
40757          */
40758         /**
40759          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
40760          */
40761         /**
40762          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
40763          */
40764         /**
40765          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
40766          */
40767         
40768         
40769         
40770         
40771     parent : false, // this might point to a 'nest' or a ???
40772     
40773     /**
40774      * Creates and adds a new region if it doesn't already exist.
40775      * @param {String} target The target region key (north, south, east, west or center).
40776      * @param {Object} config The regions config object
40777      * @return {BorderLayoutRegion} The new region
40778      */
40779     addRegion : function(config)
40780     {
40781         if(!this.regions[config.region]){
40782             var r = this.factory(config);
40783             this.bindRegion(r);
40784         }
40785         return this.regions[config.region];
40786     },
40787
40788     // private (kinda)
40789     bindRegion : function(r){
40790         this.regions[r.config.region] = r;
40791         
40792         r.on("visibilitychange",    this.layout, this);
40793         r.on("paneladded",          this.layout, this);
40794         r.on("panelremoved",        this.layout, this);
40795         r.on("invalidated",         this.layout, this);
40796         r.on("resized",             this.onRegionResized, this);
40797         r.on("collapsed",           this.onRegionCollapsed, this);
40798         r.on("expanded",            this.onRegionExpanded, this);
40799     },
40800
40801     /**
40802      * Performs a layout update.
40803      */
40804     layout : function()
40805     {
40806         if(this.updating) {
40807             return;
40808         }
40809         
40810         // render all the rebions if they have not been done alreayd?
40811         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
40812             if(this.regions[region] && !this.regions[region].bodyEl){
40813                 this.regions[region].onRender(this.el)
40814             }
40815         },this);
40816         
40817         var size = this.getViewSize();
40818         var w = size.width;
40819         var h = size.height;
40820         var centerW = w;
40821         var centerH = h;
40822         var centerY = 0;
40823         var centerX = 0;
40824         //var x = 0, y = 0;
40825
40826         var rs = this.regions;
40827         var north = rs["north"];
40828         var south = rs["south"]; 
40829         var west = rs["west"];
40830         var east = rs["east"];
40831         var center = rs["center"];
40832         //if(this.hideOnLayout){ // not supported anymore
40833             //c.el.setStyle("display", "none");
40834         //}
40835         if(north && north.isVisible()){
40836             var b = north.getBox();
40837             var m = north.getMargins();
40838             b.width = w - (m.left+m.right);
40839             b.x = m.left;
40840             b.y = m.top;
40841             centerY = b.height + b.y + m.bottom;
40842             centerH -= centerY;
40843             north.updateBox(this.safeBox(b));
40844         }
40845         if(south && south.isVisible()){
40846             var b = south.getBox();
40847             var m = south.getMargins();
40848             b.width = w - (m.left+m.right);
40849             b.x = m.left;
40850             var totalHeight = (b.height + m.top + m.bottom);
40851             b.y = h - totalHeight + m.top;
40852             centerH -= totalHeight;
40853             south.updateBox(this.safeBox(b));
40854         }
40855         if(west && west.isVisible()){
40856             var b = west.getBox();
40857             var m = west.getMargins();
40858             b.height = centerH - (m.top+m.bottom);
40859             b.x = m.left;
40860             b.y = centerY + m.top;
40861             var totalWidth = (b.width + m.left + m.right);
40862             centerX += totalWidth;
40863             centerW -= totalWidth;
40864             west.updateBox(this.safeBox(b));
40865         }
40866         if(east && east.isVisible()){
40867             var b = east.getBox();
40868             var m = east.getMargins();
40869             b.height = centerH - (m.top+m.bottom);
40870             var totalWidth = (b.width + m.left + m.right);
40871             b.x = w - totalWidth + m.left;
40872             b.y = centerY + m.top;
40873             centerW -= totalWidth;
40874             east.updateBox(this.safeBox(b));
40875         }
40876         if(center){
40877             var m = center.getMargins();
40878             var centerBox = {
40879                 x: centerX + m.left,
40880                 y: centerY + m.top,
40881                 width: centerW - (m.left+m.right),
40882                 height: centerH - (m.top+m.bottom)
40883             };
40884             //if(this.hideOnLayout){
40885                 //center.el.setStyle("display", "block");
40886             //}
40887             center.updateBox(this.safeBox(centerBox));
40888         }
40889         this.el.repaint();
40890         this.fireEvent("layout", this);
40891     },
40892
40893     // private
40894     safeBox : function(box){
40895         box.width = Math.max(0, box.width);
40896         box.height = Math.max(0, box.height);
40897         return box;
40898     },
40899
40900     /**
40901      * Adds a ContentPanel (or subclass) to this layout.
40902      * @param {String} target The target region key (north, south, east, west or center).
40903      * @param {Roo.ContentPanel} panel The panel to add
40904      * @return {Roo.ContentPanel} The added panel
40905      */
40906     add : function(target, panel){
40907          
40908         target = target.toLowerCase();
40909         return this.regions[target].add(panel);
40910     },
40911
40912     /**
40913      * Remove a ContentPanel (or subclass) to this layout.
40914      * @param {String} target The target region key (north, south, east, west or center).
40915      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
40916      * @return {Roo.ContentPanel} The removed panel
40917      */
40918     remove : function(target, panel){
40919         target = target.toLowerCase();
40920         return this.regions[target].remove(panel);
40921     },
40922
40923     /**
40924      * Searches all regions for a panel with the specified id
40925      * @param {String} panelId
40926      * @return {Roo.ContentPanel} The panel or null if it wasn't found
40927      */
40928     findPanel : function(panelId){
40929         var rs = this.regions;
40930         for(var target in rs){
40931             if(typeof rs[target] != "function"){
40932                 var p = rs[target].getPanel(panelId);
40933                 if(p){
40934                     return p;
40935                 }
40936             }
40937         }
40938         return null;
40939     },
40940
40941     /**
40942      * Searches all regions for a panel with the specified id and activates (shows) it.
40943      * @param {String/ContentPanel} panelId The panels id or the panel itself
40944      * @return {Roo.ContentPanel} The shown panel or null
40945      */
40946     showPanel : function(panelId) {
40947       var rs = this.regions;
40948       for(var target in rs){
40949          var r = rs[target];
40950          if(typeof r != "function"){
40951             if(r.hasPanel(panelId)){
40952                return r.showPanel(panelId);
40953             }
40954          }
40955       }
40956       return null;
40957    },
40958
40959    /**
40960      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
40961      * @param {Roo.state.Provider} provider (optional) An alternate state provider
40962      */
40963    /*
40964     restoreState : function(provider){
40965         if(!provider){
40966             provider = Roo.state.Manager;
40967         }
40968         var sm = new Roo.LayoutStateManager();
40969         sm.init(this, provider);
40970     },
40971 */
40972  
40973  
40974     /**
40975      * Adds a xtype elements to the layout.
40976      * <pre><code>
40977
40978 layout.addxtype({
40979        xtype : 'ContentPanel',
40980        region: 'west',
40981        items: [ .... ]
40982    }
40983 );
40984
40985 layout.addxtype({
40986         xtype : 'NestedLayoutPanel',
40987         region: 'west',
40988         layout: {
40989            center: { },
40990            west: { }   
40991         },
40992         items : [ ... list of content panels or nested layout panels.. ]
40993    }
40994 );
40995 </code></pre>
40996      * @param {Object} cfg Xtype definition of item to add.
40997      */
40998     addxtype : function(cfg)
40999     {
41000         // basically accepts a pannel...
41001         // can accept a layout region..!?!?
41002         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
41003         
41004         
41005         // theory?  children can only be panels??
41006         
41007         //if (!cfg.xtype.match(/Panel$/)) {
41008         //    return false;
41009         //}
41010         var ret = false;
41011         
41012         if (typeof(cfg.region) == 'undefined') {
41013             Roo.log("Failed to add Panel, region was not set");
41014             Roo.log(cfg);
41015             return false;
41016         }
41017         var region = cfg.region;
41018         delete cfg.region;
41019         
41020           
41021         var xitems = [];
41022         if (cfg.items) {
41023             xitems = cfg.items;
41024             delete cfg.items;
41025         }
41026         var nb = false;
41027         
41028         if ( region == 'center') {
41029             Roo.log("Center: " + cfg.title);
41030         }
41031         
41032         
41033         switch(cfg.xtype) 
41034         {
41035             case 'Content':  // ContentPanel (el, cfg)
41036             case 'Scroll':  // ContentPanel (el, cfg)
41037             case 'View': 
41038                 cfg.autoCreate = cfg.autoCreate || true;
41039                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41040                 //} else {
41041                 //    var el = this.el.createChild();
41042                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
41043                 //}
41044                 
41045                 this.add(region, ret);
41046                 break;
41047             
41048             /*
41049             case 'TreePanel': // our new panel!
41050                 cfg.el = this.el.createChild();
41051                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41052                 this.add(region, ret);
41053                 break;
41054             */
41055             
41056             case 'Nest': 
41057                 // create a new Layout (which is  a Border Layout...
41058                 
41059                 var clayout = cfg.layout;
41060                 clayout.el  = this.el.createChild();
41061                 clayout.items   = clayout.items  || [];
41062                 
41063                 delete cfg.layout;
41064                 
41065                 // replace this exitems with the clayout ones..
41066                 xitems = clayout.items;
41067                  
41068                 // force background off if it's in center...
41069                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
41070                     cfg.background = false;
41071                 }
41072                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
41073                 
41074                 
41075                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41076                 //console.log('adding nested layout panel '  + cfg.toSource());
41077                 this.add(region, ret);
41078                 nb = {}; /// find first...
41079                 break;
41080             
41081             case 'Grid':
41082                 
41083                 // needs grid and region
41084                 
41085                 //var el = this.getRegion(region).el.createChild();
41086                 /*
41087                  *var el = this.el.createChild();
41088                 // create the grid first...
41089                 cfg.grid.container = el;
41090                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
41091                 */
41092                 
41093                 if (region == 'center' && this.active ) {
41094                     cfg.background = false;
41095                 }
41096                 
41097                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41098                 
41099                 this.add(region, ret);
41100                 /*
41101                 if (cfg.background) {
41102                     // render grid on panel activation (if panel background)
41103                     ret.on('activate', function(gp) {
41104                         if (!gp.grid.rendered) {
41105                     //        gp.grid.render(el);
41106                         }
41107                     });
41108                 } else {
41109                   //  cfg.grid.render(el);
41110                 }
41111                 */
41112                 break;
41113            
41114            
41115             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
41116                 // it was the old xcomponent building that caused this before.
41117                 // espeically if border is the top element in the tree.
41118                 ret = this;
41119                 break; 
41120                 
41121                     
41122                 
41123                 
41124                 
41125             default:
41126                 /*
41127                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
41128                     
41129                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41130                     this.add(region, ret);
41131                 } else {
41132                 */
41133                     Roo.log(cfg);
41134                     throw "Can not add '" + cfg.xtype + "' to Border";
41135                     return null;
41136              
41137                                 
41138              
41139         }
41140         this.beginUpdate();
41141         // add children..
41142         var region = '';
41143         var abn = {};
41144         Roo.each(xitems, function(i)  {
41145             region = nb && i.region ? i.region : false;
41146             
41147             var add = ret.addxtype(i);
41148            
41149             if (region) {
41150                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
41151                 if (!i.background) {
41152                     abn[region] = nb[region] ;
41153                 }
41154             }
41155             
41156         });
41157         this.endUpdate();
41158
41159         // make the last non-background panel active..
41160         //if (nb) { Roo.log(abn); }
41161         if (nb) {
41162             
41163             for(var r in abn) {
41164                 region = this.getRegion(r);
41165                 if (region) {
41166                     // tried using nb[r], but it does not work..
41167                      
41168                     region.showPanel(abn[r]);
41169                    
41170                 }
41171             }
41172         }
41173         return ret;
41174         
41175     },
41176     
41177     
41178 // private
41179     factory : function(cfg)
41180     {
41181         
41182         var validRegions = Roo.bootstrap.layout.Border.regions;
41183
41184         var target = cfg.region;
41185         cfg.mgr = this;
41186         
41187         var r = Roo.bootstrap.layout;
41188         Roo.log(target);
41189         switch(target){
41190             case "north":
41191                 return new r.North(cfg);
41192             case "south":
41193                 return new r.South(cfg);
41194             case "east":
41195                 return new r.East(cfg);
41196             case "west":
41197                 return new r.West(cfg);
41198             case "center":
41199                 return new r.Center(cfg);
41200         }
41201         throw 'Layout region "'+target+'" not supported.';
41202     }
41203     
41204     
41205 });
41206  /*
41207  * Based on:
41208  * Ext JS Library 1.1.1
41209  * Copyright(c) 2006-2007, Ext JS, LLC.
41210  *
41211  * Originally Released Under LGPL - original licence link has changed is not relivant.
41212  *
41213  * Fork - LGPL
41214  * <script type="text/javascript">
41215  */
41216  
41217 /**
41218  * @class Roo.bootstrap.layout.Basic
41219  * @extends Roo.util.Observable
41220  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
41221  * and does not have a titlebar, tabs or any other features. All it does is size and position 
41222  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
41223  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
41224  * @cfg {string}   region  the region that it inhabits..
41225  * @cfg {bool}   skipConfig skip config?
41226  * 
41227
41228  */
41229 Roo.bootstrap.layout.Basic = function(config){
41230     
41231     this.mgr = config.mgr;
41232     
41233     this.position = config.region;
41234     
41235     var skipConfig = config.skipConfig;
41236     
41237     this.events = {
41238         /**
41239          * @scope Roo.BasicLayoutRegion
41240          */
41241         
41242         /**
41243          * @event beforeremove
41244          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
41245          * @param {Roo.LayoutRegion} this
41246          * @param {Roo.ContentPanel} panel The panel
41247          * @param {Object} e The cancel event object
41248          */
41249         "beforeremove" : true,
41250         /**
41251          * @event invalidated
41252          * Fires when the layout for this region is changed.
41253          * @param {Roo.LayoutRegion} this
41254          */
41255         "invalidated" : true,
41256         /**
41257          * @event visibilitychange
41258          * Fires when this region is shown or hidden 
41259          * @param {Roo.LayoutRegion} this
41260          * @param {Boolean} visibility true or false
41261          */
41262         "visibilitychange" : true,
41263         /**
41264          * @event paneladded
41265          * Fires when a panel is added. 
41266          * @param {Roo.LayoutRegion} this
41267          * @param {Roo.ContentPanel} panel The panel
41268          */
41269         "paneladded" : true,
41270         /**
41271          * @event panelremoved
41272          * Fires when a panel is removed. 
41273          * @param {Roo.LayoutRegion} this
41274          * @param {Roo.ContentPanel} panel The panel
41275          */
41276         "panelremoved" : true,
41277         /**
41278          * @event beforecollapse
41279          * Fires when this region before collapse.
41280          * @param {Roo.LayoutRegion} this
41281          */
41282         "beforecollapse" : true,
41283         /**
41284          * @event collapsed
41285          * Fires when this region is collapsed.
41286          * @param {Roo.LayoutRegion} this
41287          */
41288         "collapsed" : true,
41289         /**
41290          * @event expanded
41291          * Fires when this region is expanded.
41292          * @param {Roo.LayoutRegion} this
41293          */
41294         "expanded" : true,
41295         /**
41296          * @event slideshow
41297          * Fires when this region is slid into view.
41298          * @param {Roo.LayoutRegion} this
41299          */
41300         "slideshow" : true,
41301         /**
41302          * @event slidehide
41303          * Fires when this region slides out of view. 
41304          * @param {Roo.LayoutRegion} this
41305          */
41306         "slidehide" : true,
41307         /**
41308          * @event panelactivated
41309          * Fires when a panel is activated. 
41310          * @param {Roo.LayoutRegion} this
41311          * @param {Roo.ContentPanel} panel The activated panel
41312          */
41313         "panelactivated" : true,
41314         /**
41315          * @event resized
41316          * Fires when the user resizes this region. 
41317          * @param {Roo.LayoutRegion} this
41318          * @param {Number} newSize The new size (width for east/west, height for north/south)
41319          */
41320         "resized" : true
41321     };
41322     /** A collection of panels in this region. @type Roo.util.MixedCollection */
41323     this.panels = new Roo.util.MixedCollection();
41324     this.panels.getKey = this.getPanelId.createDelegate(this);
41325     this.box = null;
41326     this.activePanel = null;
41327     // ensure listeners are added...
41328     
41329     if (config.listeners || config.events) {
41330         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
41331             listeners : config.listeners || {},
41332             events : config.events || {}
41333         });
41334     }
41335     
41336     if(skipConfig !== true){
41337         this.applyConfig(config);
41338     }
41339 };
41340
41341 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
41342 {
41343     getPanelId : function(p){
41344         return p.getId();
41345     },
41346     
41347     applyConfig : function(config){
41348         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41349         this.config = config;
41350         
41351     },
41352     
41353     /**
41354      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
41355      * the width, for horizontal (north, south) the height.
41356      * @param {Number} newSize The new width or height
41357      */
41358     resizeTo : function(newSize){
41359         var el = this.el ? this.el :
41360                  (this.activePanel ? this.activePanel.getEl() : null);
41361         if(el){
41362             switch(this.position){
41363                 case "east":
41364                 case "west":
41365                     el.setWidth(newSize);
41366                     this.fireEvent("resized", this, newSize);
41367                 break;
41368                 case "north":
41369                 case "south":
41370                     el.setHeight(newSize);
41371                     this.fireEvent("resized", this, newSize);
41372                 break;                
41373             }
41374         }
41375     },
41376     
41377     getBox : function(){
41378         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
41379     },
41380     
41381     getMargins : function(){
41382         return this.margins;
41383     },
41384     
41385     updateBox : function(box){
41386         this.box = box;
41387         var el = this.activePanel.getEl();
41388         el.dom.style.left = box.x + "px";
41389         el.dom.style.top = box.y + "px";
41390         this.activePanel.setSize(box.width, box.height);
41391     },
41392     
41393     /**
41394      * Returns the container element for this region.
41395      * @return {Roo.Element}
41396      */
41397     getEl : function(){
41398         return this.activePanel;
41399     },
41400     
41401     /**
41402      * Returns true if this region is currently visible.
41403      * @return {Boolean}
41404      */
41405     isVisible : function(){
41406         return this.activePanel ? true : false;
41407     },
41408     
41409     setActivePanel : function(panel){
41410         panel = this.getPanel(panel);
41411         if(this.activePanel && this.activePanel != panel){
41412             this.activePanel.setActiveState(false);
41413             this.activePanel.getEl().setLeftTop(-10000,-10000);
41414         }
41415         this.activePanel = panel;
41416         panel.setActiveState(true);
41417         if(this.box){
41418             panel.setSize(this.box.width, this.box.height);
41419         }
41420         this.fireEvent("panelactivated", this, panel);
41421         this.fireEvent("invalidated");
41422     },
41423     
41424     /**
41425      * Show the specified panel.
41426      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
41427      * @return {Roo.ContentPanel} The shown panel or null
41428      */
41429     showPanel : function(panel){
41430         panel = this.getPanel(panel);
41431         if(panel){
41432             this.setActivePanel(panel);
41433         }
41434         return panel;
41435     },
41436     
41437     /**
41438      * Get the active panel for this region.
41439      * @return {Roo.ContentPanel} The active panel or null
41440      */
41441     getActivePanel : function(){
41442         return this.activePanel;
41443     },
41444     
41445     /**
41446      * Add the passed ContentPanel(s)
41447      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
41448      * @return {Roo.ContentPanel} The panel added (if only one was added)
41449      */
41450     add : function(panel){
41451         if(arguments.length > 1){
41452             for(var i = 0, len = arguments.length; i < len; i++) {
41453                 this.add(arguments[i]);
41454             }
41455             return null;
41456         }
41457         if(this.hasPanel(panel)){
41458             this.showPanel(panel);
41459             return panel;
41460         }
41461         var el = panel.getEl();
41462         if(el.dom.parentNode != this.mgr.el.dom){
41463             this.mgr.el.dom.appendChild(el.dom);
41464         }
41465         if(panel.setRegion){
41466             panel.setRegion(this);
41467         }
41468         this.panels.add(panel);
41469         el.setStyle("position", "absolute");
41470         if(!panel.background){
41471             this.setActivePanel(panel);
41472             if(this.config.initialSize && this.panels.getCount()==1){
41473                 this.resizeTo(this.config.initialSize);
41474             }
41475         }
41476         this.fireEvent("paneladded", this, panel);
41477         return panel;
41478     },
41479     
41480     /**
41481      * Returns true if the panel is in this region.
41482      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41483      * @return {Boolean}
41484      */
41485     hasPanel : function(panel){
41486         if(typeof panel == "object"){ // must be panel obj
41487             panel = panel.getId();
41488         }
41489         return this.getPanel(panel) ? true : false;
41490     },
41491     
41492     /**
41493      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
41494      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41495      * @param {Boolean} preservePanel Overrides the config preservePanel option
41496      * @return {Roo.ContentPanel} The panel that was removed
41497      */
41498     remove : function(panel, preservePanel){
41499         panel = this.getPanel(panel);
41500         if(!panel){
41501             return null;
41502         }
41503         var e = {};
41504         this.fireEvent("beforeremove", this, panel, e);
41505         if(e.cancel === true){
41506             return null;
41507         }
41508         var panelId = panel.getId();
41509         this.panels.removeKey(panelId);
41510         return panel;
41511     },
41512     
41513     /**
41514      * Returns the panel specified or null if it's not in this region.
41515      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41516      * @return {Roo.ContentPanel}
41517      */
41518     getPanel : function(id){
41519         if(typeof id == "object"){ // must be panel obj
41520             return id;
41521         }
41522         return this.panels.get(id);
41523     },
41524     
41525     /**
41526      * Returns this regions position (north/south/east/west/center).
41527      * @return {String} 
41528      */
41529     getPosition: function(){
41530         return this.position;    
41531     }
41532 });/*
41533  * Based on:
41534  * Ext JS Library 1.1.1
41535  * Copyright(c) 2006-2007, Ext JS, LLC.
41536  *
41537  * Originally Released Under LGPL - original licence link has changed is not relivant.
41538  *
41539  * Fork - LGPL
41540  * <script type="text/javascript">
41541  */
41542  
41543 /**
41544  * @class Roo.bootstrap.layout.Region
41545  * @extends Roo.bootstrap.layout.Basic
41546  * This class represents a region in a layout manager.
41547  
41548  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
41549  * @cfg {Object}    cmargins        Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
41550  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
41551  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
41552  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
41553  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
41554  * @cfg {String}    title           The title for the region (overrides panel titles)
41555  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
41556  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
41557  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
41558  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
41559  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
41560  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
41561  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
41562  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
41563  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
41564  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
41565
41566  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
41567  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
41568  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
41569  * @cfg {Number}    width           For East/West panels
41570  * @cfg {Number}    height          For North/South panels
41571  * @cfg {Boolean}   split           To show the splitter
41572  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
41573  * 
41574  * @cfg {string}   cls             Extra CSS classes to add to region
41575  * 
41576  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
41577  * @cfg {string}   region  the region that it inhabits..
41578  *
41579
41580  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
41581  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
41582
41583  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
41584  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
41585  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
41586  */
41587 Roo.bootstrap.layout.Region = function(config)
41588 {
41589     this.applyConfig(config);
41590
41591     var mgr = config.mgr;
41592     var pos = config.region;
41593     config.skipConfig = true;
41594     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
41595     
41596     if (mgr.el) {
41597         this.onRender(mgr.el);   
41598     }
41599      
41600     this.visible = true;
41601     this.collapsed = false;
41602     this.unrendered_panels = [];
41603 };
41604
41605 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
41606
41607     position: '', // set by wrapper (eg. north/south etc..)
41608     unrendered_panels : null,  // unrendered panels.
41609     
41610     tabPosition : false,
41611     
41612     mgr: false, // points to 'Border'
41613     
41614     
41615     createBody : function(){
41616         /** This region's body element 
41617         * @type Roo.Element */
41618         this.bodyEl = this.el.createChild({
41619                 tag: "div",
41620                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
41621         });
41622     },
41623
41624     onRender: function(ctr, pos)
41625     {
41626         var dh = Roo.DomHelper;
41627         /** This region's container element 
41628         * @type Roo.Element */
41629         this.el = dh.append(ctr.dom, {
41630                 tag: "div",
41631                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
41632             }, true);
41633         /** This region's title element 
41634         * @type Roo.Element */
41635     
41636         this.titleEl = dh.append(this.el.dom,  {
41637                 tag: "div",
41638                 unselectable: "on",
41639                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
41640                 children:[
41641                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
41642                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
41643                 ]
41644             }, true);
41645         
41646         this.titleEl.enableDisplayMode();
41647         /** This region's title text element 
41648         * @type HTMLElement */
41649         this.titleTextEl = this.titleEl.dom.firstChild;
41650         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
41651         /*
41652         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
41653         this.closeBtn.enableDisplayMode();
41654         this.closeBtn.on("click", this.closeClicked, this);
41655         this.closeBtn.hide();
41656     */
41657         this.createBody(this.config);
41658         if(this.config.hideWhenEmpty){
41659             this.hide();
41660             this.on("paneladded", this.validateVisibility, this);
41661             this.on("panelremoved", this.validateVisibility, this);
41662         }
41663         if(this.autoScroll){
41664             this.bodyEl.setStyle("overflow", "auto");
41665         }else{
41666             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
41667         }
41668         //if(c.titlebar !== false){
41669             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
41670                 this.titleEl.hide();
41671             }else{
41672                 this.titleEl.show();
41673                 if(this.config.title){
41674                     this.titleTextEl.innerHTML = this.config.title;
41675                 }
41676             }
41677         //}
41678         if(this.config.collapsed){
41679             this.collapse(true);
41680         }
41681         if(this.config.hidden){
41682             this.hide();
41683         }
41684         
41685         if (this.unrendered_panels && this.unrendered_panels.length) {
41686             for (var i =0;i< this.unrendered_panels.length; i++) {
41687                 this.add(this.unrendered_panels[i]);
41688             }
41689             this.unrendered_panels = null;
41690             
41691         }
41692         
41693     },
41694     
41695     applyConfig : function(c)
41696     {
41697         /*
41698          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
41699             var dh = Roo.DomHelper;
41700             if(c.titlebar !== false){
41701                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
41702                 this.collapseBtn.on("click", this.collapse, this);
41703                 this.collapseBtn.enableDisplayMode();
41704                 /*
41705                 if(c.showPin === true || this.showPin){
41706                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
41707                     this.stickBtn.enableDisplayMode();
41708                     this.stickBtn.on("click", this.expand, this);
41709                     this.stickBtn.hide();
41710                 }
41711                 
41712             }
41713             */
41714             /** This region's collapsed element
41715             * @type Roo.Element */
41716             /*
41717              *
41718             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
41719                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
41720             ]}, true);
41721             
41722             if(c.floatable !== false){
41723                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
41724                this.collapsedEl.on("click", this.collapseClick, this);
41725             }
41726
41727             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
41728                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
41729                    id: "message", unselectable: "on", style:{"float":"left"}});
41730                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
41731              }
41732             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
41733             this.expandBtn.on("click", this.expand, this);
41734             
41735         }
41736         
41737         if(this.collapseBtn){
41738             this.collapseBtn.setVisible(c.collapsible == true);
41739         }
41740         
41741         this.cmargins = c.cmargins || this.cmargins ||
41742                          (this.position == "west" || this.position == "east" ?
41743                              {top: 0, left: 2, right:2, bottom: 0} :
41744                              {top: 2, left: 0, right:0, bottom: 2});
41745         */
41746         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41747         
41748         
41749         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
41750         
41751         this.autoScroll = c.autoScroll || false;
41752         
41753         
41754        
41755         
41756         this.duration = c.duration || .30;
41757         this.slideDuration = c.slideDuration || .45;
41758         this.config = c;
41759        
41760     },
41761     /**
41762      * Returns true if this region is currently visible.
41763      * @return {Boolean}
41764      */
41765     isVisible : function(){
41766         return this.visible;
41767     },
41768
41769     /**
41770      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
41771      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
41772      */
41773     //setCollapsedTitle : function(title){
41774     //    title = title || "&#160;";
41775      //   if(this.collapsedTitleTextEl){
41776       //      this.collapsedTitleTextEl.innerHTML = title;
41777        // }
41778     //},
41779
41780     getBox : function(){
41781         var b;
41782       //  if(!this.collapsed){
41783             b = this.el.getBox(false, true);
41784        // }else{
41785           //  b = this.collapsedEl.getBox(false, true);
41786         //}
41787         return b;
41788     },
41789
41790     getMargins : function(){
41791         return this.margins;
41792         //return this.collapsed ? this.cmargins : this.margins;
41793     },
41794 /*
41795     highlight : function(){
41796         this.el.addClass("x-layout-panel-dragover");
41797     },
41798
41799     unhighlight : function(){
41800         this.el.removeClass("x-layout-panel-dragover");
41801     },
41802 */
41803     updateBox : function(box)
41804     {
41805         if (!this.bodyEl) {
41806             return; // not rendered yet..
41807         }
41808         
41809         this.box = box;
41810         if(!this.collapsed){
41811             this.el.dom.style.left = box.x + "px";
41812             this.el.dom.style.top = box.y + "px";
41813             this.updateBody(box.width, box.height);
41814         }else{
41815             this.collapsedEl.dom.style.left = box.x + "px";
41816             this.collapsedEl.dom.style.top = box.y + "px";
41817             this.collapsedEl.setSize(box.width, box.height);
41818         }
41819         if(this.tabs){
41820             this.tabs.autoSizeTabs();
41821         }
41822     },
41823
41824     updateBody : function(w, h)
41825     {
41826         if(w !== null){
41827             this.el.setWidth(w);
41828             w -= this.el.getBorderWidth("rl");
41829             if(this.config.adjustments){
41830                 w += this.config.adjustments[0];
41831             }
41832         }
41833         if(h !== null && h > 0){
41834             this.el.setHeight(h);
41835             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
41836             h -= this.el.getBorderWidth("tb");
41837             if(this.config.adjustments){
41838                 h += this.config.adjustments[1];
41839             }
41840             this.bodyEl.setHeight(h);
41841             if(this.tabs){
41842                 h = this.tabs.syncHeight(h);
41843             }
41844         }
41845         if(this.panelSize){
41846             w = w !== null ? w : this.panelSize.width;
41847             h = h !== null ? h : this.panelSize.height;
41848         }
41849         if(this.activePanel){
41850             var el = this.activePanel.getEl();
41851             w = w !== null ? w : el.getWidth();
41852             h = h !== null ? h : el.getHeight();
41853             this.panelSize = {width: w, height: h};
41854             this.activePanel.setSize(w, h);
41855         }
41856         if(Roo.isIE && this.tabs){
41857             this.tabs.el.repaint();
41858         }
41859     },
41860
41861     /**
41862      * Returns the container element for this region.
41863      * @return {Roo.Element}
41864      */
41865     getEl : function(){
41866         return this.el;
41867     },
41868
41869     /**
41870      * Hides this region.
41871      */
41872     hide : function(){
41873         //if(!this.collapsed){
41874             this.el.dom.style.left = "-2000px";
41875             this.el.hide();
41876         //}else{
41877          //   this.collapsedEl.dom.style.left = "-2000px";
41878          //   this.collapsedEl.hide();
41879        // }
41880         this.visible = false;
41881         this.fireEvent("visibilitychange", this, false);
41882     },
41883
41884     /**
41885      * Shows this region if it was previously hidden.
41886      */
41887     show : function(){
41888         //if(!this.collapsed){
41889             this.el.show();
41890         //}else{
41891         //    this.collapsedEl.show();
41892        // }
41893         this.visible = true;
41894         this.fireEvent("visibilitychange", this, true);
41895     },
41896 /*
41897     closeClicked : function(){
41898         if(this.activePanel){
41899             this.remove(this.activePanel);
41900         }
41901     },
41902
41903     collapseClick : function(e){
41904         if(this.isSlid){
41905            e.stopPropagation();
41906            this.slideIn();
41907         }else{
41908            e.stopPropagation();
41909            this.slideOut();
41910         }
41911     },
41912 */
41913     /**
41914      * Collapses this region.
41915      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
41916      */
41917     /*
41918     collapse : function(skipAnim, skipCheck = false){
41919         if(this.collapsed) {
41920             return;
41921         }
41922         
41923         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
41924             
41925             this.collapsed = true;
41926             if(this.split){
41927                 this.split.el.hide();
41928             }
41929             if(this.config.animate && skipAnim !== true){
41930                 this.fireEvent("invalidated", this);
41931                 this.animateCollapse();
41932             }else{
41933                 this.el.setLocation(-20000,-20000);
41934                 this.el.hide();
41935                 this.collapsedEl.show();
41936                 this.fireEvent("collapsed", this);
41937                 this.fireEvent("invalidated", this);
41938             }
41939         }
41940         
41941     },
41942 */
41943     animateCollapse : function(){
41944         // overridden
41945     },
41946
41947     /**
41948      * Expands this region if it was previously collapsed.
41949      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
41950      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
41951      */
41952     /*
41953     expand : function(e, skipAnim){
41954         if(e) {
41955             e.stopPropagation();
41956         }
41957         if(!this.collapsed || this.el.hasActiveFx()) {
41958             return;
41959         }
41960         if(this.isSlid){
41961             this.afterSlideIn();
41962             skipAnim = true;
41963         }
41964         this.collapsed = false;
41965         if(this.config.animate && skipAnim !== true){
41966             this.animateExpand();
41967         }else{
41968             this.el.show();
41969             if(this.split){
41970                 this.split.el.show();
41971             }
41972             this.collapsedEl.setLocation(-2000,-2000);
41973             this.collapsedEl.hide();
41974             this.fireEvent("invalidated", this);
41975             this.fireEvent("expanded", this);
41976         }
41977     },
41978 */
41979     animateExpand : function(){
41980         // overridden
41981     },
41982
41983     initTabs : function()
41984     {
41985         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
41986         
41987         var ts = new Roo.bootstrap.panel.Tabs({
41988             el: this.bodyEl.dom,
41989             region : this,
41990             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
41991             disableTooltips: this.config.disableTabTips,
41992             toolbar : this.config.toolbar
41993         });
41994         
41995         if(this.config.hideTabs){
41996             ts.stripWrap.setDisplayed(false);
41997         }
41998         this.tabs = ts;
41999         ts.resizeTabs = this.config.resizeTabs === true;
42000         ts.minTabWidth = this.config.minTabWidth || 40;
42001         ts.maxTabWidth = this.config.maxTabWidth || 250;
42002         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
42003         ts.monitorResize = false;
42004         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
42005         ts.bodyEl.addClass('roo-layout-tabs-body');
42006         this.panels.each(this.initPanelAsTab, this);
42007     },
42008
42009     initPanelAsTab : function(panel){
42010         var ti = this.tabs.addTab(
42011             panel.getEl().id,
42012             panel.getTitle(),
42013             null,
42014             this.config.closeOnTab && panel.isClosable(),
42015             panel.tpl
42016         );
42017         if(panel.tabTip !== undefined){
42018             ti.setTooltip(panel.tabTip);
42019         }
42020         ti.on("activate", function(){
42021               this.setActivePanel(panel);
42022         }, this);
42023         
42024         if(this.config.closeOnTab){
42025             ti.on("beforeclose", function(t, e){
42026                 e.cancel = true;
42027                 this.remove(panel);
42028             }, this);
42029         }
42030         
42031         panel.tabItem = ti;
42032         
42033         return ti;
42034     },
42035
42036     updatePanelTitle : function(panel, title)
42037     {
42038         if(this.activePanel == panel){
42039             this.updateTitle(title);
42040         }
42041         if(this.tabs){
42042             var ti = this.tabs.getTab(panel.getEl().id);
42043             ti.setText(title);
42044             if(panel.tabTip !== undefined){
42045                 ti.setTooltip(panel.tabTip);
42046             }
42047         }
42048     },
42049
42050     updateTitle : function(title){
42051         if(this.titleTextEl && !this.config.title){
42052             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
42053         }
42054     },
42055
42056     setActivePanel : function(panel)
42057     {
42058         panel = this.getPanel(panel);
42059         if(this.activePanel && this.activePanel != panel){
42060             if(this.activePanel.setActiveState(false) === false){
42061                 return;
42062             }
42063         }
42064         this.activePanel = panel;
42065         panel.setActiveState(true);
42066         if(this.panelSize){
42067             panel.setSize(this.panelSize.width, this.panelSize.height);
42068         }
42069         if(this.closeBtn){
42070             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
42071         }
42072         this.updateTitle(panel.getTitle());
42073         if(this.tabs){
42074             this.fireEvent("invalidated", this);
42075         }
42076         this.fireEvent("panelactivated", this, panel);
42077     },
42078
42079     /**
42080      * Shows the specified panel.
42081      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
42082      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
42083      */
42084     showPanel : function(panel)
42085     {
42086         panel = this.getPanel(panel);
42087         if(panel){
42088             if(this.tabs){
42089                 var tab = this.tabs.getTab(panel.getEl().id);
42090                 if(tab.isHidden()){
42091                     this.tabs.unhideTab(tab.id);
42092                 }
42093                 tab.activate();
42094             }else{
42095                 this.setActivePanel(panel);
42096             }
42097         }
42098         return panel;
42099     },
42100
42101     /**
42102      * Get the active panel for this region.
42103      * @return {Roo.ContentPanel} The active panel or null
42104      */
42105     getActivePanel : function(){
42106         return this.activePanel;
42107     },
42108
42109     validateVisibility : function(){
42110         if(this.panels.getCount() < 1){
42111             this.updateTitle("&#160;");
42112             this.closeBtn.hide();
42113             this.hide();
42114         }else{
42115             if(!this.isVisible()){
42116                 this.show();
42117             }
42118         }
42119     },
42120
42121     /**
42122      * Adds the passed ContentPanel(s) to this region.
42123      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42124      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
42125      */
42126     add : function(panel)
42127     {
42128         if(arguments.length > 1){
42129             for(var i = 0, len = arguments.length; i < len; i++) {
42130                 this.add(arguments[i]);
42131             }
42132             return null;
42133         }
42134         
42135         // if we have not been rendered yet, then we can not really do much of this..
42136         if (!this.bodyEl) {
42137             this.unrendered_panels.push(panel);
42138             return panel;
42139         }
42140         
42141         
42142         
42143         
42144         if(this.hasPanel(panel)){
42145             this.showPanel(panel);
42146             return panel;
42147         }
42148         panel.setRegion(this);
42149         this.panels.add(panel);
42150        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
42151             // sinle panel - no tab...?? would it not be better to render it with the tabs,
42152             // and hide them... ???
42153             this.bodyEl.dom.appendChild(panel.getEl().dom);
42154             if(panel.background !== true){
42155                 this.setActivePanel(panel);
42156             }
42157             this.fireEvent("paneladded", this, panel);
42158             return panel;
42159         }
42160         */
42161         if(!this.tabs){
42162             this.initTabs();
42163         }else{
42164             this.initPanelAsTab(panel);
42165         }
42166         
42167         
42168         if(panel.background !== true){
42169             this.tabs.activate(panel.getEl().id);
42170         }
42171         this.fireEvent("paneladded", this, panel);
42172         return panel;
42173     },
42174
42175     /**
42176      * Hides the tab for the specified panel.
42177      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42178      */
42179     hidePanel : function(panel){
42180         if(this.tabs && (panel = this.getPanel(panel))){
42181             this.tabs.hideTab(panel.getEl().id);
42182         }
42183     },
42184
42185     /**
42186      * Unhides the tab for a previously hidden panel.
42187      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42188      */
42189     unhidePanel : function(panel){
42190         if(this.tabs && (panel = this.getPanel(panel))){
42191             this.tabs.unhideTab(panel.getEl().id);
42192         }
42193     },
42194
42195     clearPanels : function(){
42196         while(this.panels.getCount() > 0){
42197              this.remove(this.panels.first());
42198         }
42199     },
42200
42201     /**
42202      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
42203      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42204      * @param {Boolean} preservePanel Overrides the config preservePanel option
42205      * @return {Roo.ContentPanel} The panel that was removed
42206      */
42207     remove : function(panel, preservePanel)
42208     {
42209         panel = this.getPanel(panel);
42210         if(!panel){
42211             return null;
42212         }
42213         var e = {};
42214         this.fireEvent("beforeremove", this, panel, e);
42215         if(e.cancel === true){
42216             return null;
42217         }
42218         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
42219         var panelId = panel.getId();
42220         this.panels.removeKey(panelId);
42221         if(preservePanel){
42222             document.body.appendChild(panel.getEl().dom);
42223         }
42224         if(this.tabs){
42225             this.tabs.removeTab(panel.getEl().id);
42226         }else if (!preservePanel){
42227             this.bodyEl.dom.removeChild(panel.getEl().dom);
42228         }
42229         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
42230             var p = this.panels.first();
42231             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
42232             tempEl.appendChild(p.getEl().dom);
42233             this.bodyEl.update("");
42234             this.bodyEl.dom.appendChild(p.getEl().dom);
42235             tempEl = null;
42236             this.updateTitle(p.getTitle());
42237             this.tabs = null;
42238             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
42239             this.setActivePanel(p);
42240         }
42241         panel.setRegion(null);
42242         if(this.activePanel == panel){
42243             this.activePanel = null;
42244         }
42245         if(this.config.autoDestroy !== false && preservePanel !== true){
42246             try{panel.destroy();}catch(e){}
42247         }
42248         this.fireEvent("panelremoved", this, panel);
42249         return panel;
42250     },
42251
42252     /**
42253      * Returns the TabPanel component used by this region
42254      * @return {Roo.TabPanel}
42255      */
42256     getTabs : function(){
42257         return this.tabs;
42258     },
42259
42260     createTool : function(parentEl, className){
42261         var btn = Roo.DomHelper.append(parentEl, {
42262             tag: "div",
42263             cls: "x-layout-tools-button",
42264             children: [ {
42265                 tag: "div",
42266                 cls: "roo-layout-tools-button-inner " + className,
42267                 html: "&#160;"
42268             }]
42269         }, true);
42270         btn.addClassOnOver("roo-layout-tools-button-over");
42271         return btn;
42272     }
42273 });/*
42274  * Based on:
42275  * Ext JS Library 1.1.1
42276  * Copyright(c) 2006-2007, Ext JS, LLC.
42277  *
42278  * Originally Released Under LGPL - original licence link has changed is not relivant.
42279  *
42280  * Fork - LGPL
42281  * <script type="text/javascript">
42282  */
42283  
42284
42285
42286 /**
42287  * @class Roo.SplitLayoutRegion
42288  * @extends Roo.LayoutRegion
42289  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
42290  */
42291 Roo.bootstrap.layout.Split = function(config){
42292     this.cursor = config.cursor;
42293     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
42294 };
42295
42296 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
42297 {
42298     splitTip : "Drag to resize.",
42299     collapsibleSplitTip : "Drag to resize. Double click to hide.",
42300     useSplitTips : false,
42301
42302     applyConfig : function(config){
42303         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
42304     },
42305     
42306     onRender : function(ctr,pos) {
42307         
42308         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
42309         if(!this.config.split){
42310             return;
42311         }
42312         if(!this.split){
42313             
42314             var splitEl = Roo.DomHelper.append(ctr.dom,  {
42315                             tag: "div",
42316                             id: this.el.id + "-split",
42317                             cls: "roo-layout-split roo-layout-split-"+this.position,
42318                             html: "&#160;"
42319             });
42320             /** The SplitBar for this region 
42321             * @type Roo.SplitBar */
42322             // does not exist yet...
42323             Roo.log([this.position, this.orientation]);
42324             
42325             this.split = new Roo.bootstrap.SplitBar({
42326                 dragElement : splitEl,
42327                 resizingElement: this.el,
42328                 orientation : this.orientation
42329             });
42330             
42331             this.split.on("moved", this.onSplitMove, this);
42332             this.split.useShim = this.config.useShim === true;
42333             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
42334             if(this.useSplitTips){
42335                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
42336             }
42337             //if(config.collapsible){
42338             //    this.split.el.on("dblclick", this.collapse,  this);
42339             //}
42340         }
42341         if(typeof this.config.minSize != "undefined"){
42342             this.split.minSize = this.config.minSize;
42343         }
42344         if(typeof this.config.maxSize != "undefined"){
42345             this.split.maxSize = this.config.maxSize;
42346         }
42347         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
42348             this.hideSplitter();
42349         }
42350         
42351     },
42352
42353     getHMaxSize : function(){
42354          var cmax = this.config.maxSize || 10000;
42355          var center = this.mgr.getRegion("center");
42356          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
42357     },
42358
42359     getVMaxSize : function(){
42360          var cmax = this.config.maxSize || 10000;
42361          var center = this.mgr.getRegion("center");
42362          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
42363     },
42364
42365     onSplitMove : function(split, newSize){
42366         this.fireEvent("resized", this, newSize);
42367     },
42368     
42369     /** 
42370      * Returns the {@link Roo.SplitBar} for this region.
42371      * @return {Roo.SplitBar}
42372      */
42373     getSplitBar : function(){
42374         return this.split;
42375     },
42376     
42377     hide : function(){
42378         this.hideSplitter();
42379         Roo.bootstrap.layout.Split.superclass.hide.call(this);
42380     },
42381
42382     hideSplitter : function(){
42383         if(this.split){
42384             this.split.el.setLocation(-2000,-2000);
42385             this.split.el.hide();
42386         }
42387     },
42388
42389     show : function(){
42390         if(this.split){
42391             this.split.el.show();
42392         }
42393         Roo.bootstrap.layout.Split.superclass.show.call(this);
42394     },
42395     
42396     beforeSlide: function(){
42397         if(Roo.isGecko){// firefox overflow auto bug workaround
42398             this.bodyEl.clip();
42399             if(this.tabs) {
42400                 this.tabs.bodyEl.clip();
42401             }
42402             if(this.activePanel){
42403                 this.activePanel.getEl().clip();
42404                 
42405                 if(this.activePanel.beforeSlide){
42406                     this.activePanel.beforeSlide();
42407                 }
42408             }
42409         }
42410     },
42411     
42412     afterSlide : function(){
42413         if(Roo.isGecko){// firefox overflow auto bug workaround
42414             this.bodyEl.unclip();
42415             if(this.tabs) {
42416                 this.tabs.bodyEl.unclip();
42417             }
42418             if(this.activePanel){
42419                 this.activePanel.getEl().unclip();
42420                 if(this.activePanel.afterSlide){
42421                     this.activePanel.afterSlide();
42422                 }
42423             }
42424         }
42425     },
42426
42427     initAutoHide : function(){
42428         if(this.autoHide !== false){
42429             if(!this.autoHideHd){
42430                 var st = new Roo.util.DelayedTask(this.slideIn, this);
42431                 this.autoHideHd = {
42432                     "mouseout": function(e){
42433                         if(!e.within(this.el, true)){
42434                             st.delay(500);
42435                         }
42436                     },
42437                     "mouseover" : function(e){
42438                         st.cancel();
42439                     },
42440                     scope : this
42441                 };
42442             }
42443             this.el.on(this.autoHideHd);
42444         }
42445     },
42446
42447     clearAutoHide : function(){
42448         if(this.autoHide !== false){
42449             this.el.un("mouseout", this.autoHideHd.mouseout);
42450             this.el.un("mouseover", this.autoHideHd.mouseover);
42451         }
42452     },
42453
42454     clearMonitor : function(){
42455         Roo.get(document).un("click", this.slideInIf, this);
42456     },
42457
42458     // these names are backwards but not changed for compat
42459     slideOut : function(){
42460         if(this.isSlid || this.el.hasActiveFx()){
42461             return;
42462         }
42463         this.isSlid = true;
42464         if(this.collapseBtn){
42465             this.collapseBtn.hide();
42466         }
42467         this.closeBtnState = this.closeBtn.getStyle('display');
42468         this.closeBtn.hide();
42469         if(this.stickBtn){
42470             this.stickBtn.show();
42471         }
42472         this.el.show();
42473         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
42474         this.beforeSlide();
42475         this.el.setStyle("z-index", 10001);
42476         this.el.slideIn(this.getSlideAnchor(), {
42477             callback: function(){
42478                 this.afterSlide();
42479                 this.initAutoHide();
42480                 Roo.get(document).on("click", this.slideInIf, this);
42481                 this.fireEvent("slideshow", this);
42482             },
42483             scope: this,
42484             block: true
42485         });
42486     },
42487
42488     afterSlideIn : function(){
42489         this.clearAutoHide();
42490         this.isSlid = false;
42491         this.clearMonitor();
42492         this.el.setStyle("z-index", "");
42493         if(this.collapseBtn){
42494             this.collapseBtn.show();
42495         }
42496         this.closeBtn.setStyle('display', this.closeBtnState);
42497         if(this.stickBtn){
42498             this.stickBtn.hide();
42499         }
42500         this.fireEvent("slidehide", this);
42501     },
42502
42503     slideIn : function(cb){
42504         if(!this.isSlid || this.el.hasActiveFx()){
42505             Roo.callback(cb);
42506             return;
42507         }
42508         this.isSlid = false;
42509         this.beforeSlide();
42510         this.el.slideOut(this.getSlideAnchor(), {
42511             callback: function(){
42512                 this.el.setLeftTop(-10000, -10000);
42513                 this.afterSlide();
42514                 this.afterSlideIn();
42515                 Roo.callback(cb);
42516             },
42517             scope: this,
42518             block: true
42519         });
42520     },
42521     
42522     slideInIf : function(e){
42523         if(!e.within(this.el)){
42524             this.slideIn();
42525         }
42526     },
42527
42528     animateCollapse : function(){
42529         this.beforeSlide();
42530         this.el.setStyle("z-index", 20000);
42531         var anchor = this.getSlideAnchor();
42532         this.el.slideOut(anchor, {
42533             callback : function(){
42534                 this.el.setStyle("z-index", "");
42535                 this.collapsedEl.slideIn(anchor, {duration:.3});
42536                 this.afterSlide();
42537                 this.el.setLocation(-10000,-10000);
42538                 this.el.hide();
42539                 this.fireEvent("collapsed", this);
42540             },
42541             scope: this,
42542             block: true
42543         });
42544     },
42545
42546     animateExpand : function(){
42547         this.beforeSlide();
42548         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
42549         this.el.setStyle("z-index", 20000);
42550         this.collapsedEl.hide({
42551             duration:.1
42552         });
42553         this.el.slideIn(this.getSlideAnchor(), {
42554             callback : function(){
42555                 this.el.setStyle("z-index", "");
42556                 this.afterSlide();
42557                 if(this.split){
42558                     this.split.el.show();
42559                 }
42560                 this.fireEvent("invalidated", this);
42561                 this.fireEvent("expanded", this);
42562             },
42563             scope: this,
42564             block: true
42565         });
42566     },
42567
42568     anchors : {
42569         "west" : "left",
42570         "east" : "right",
42571         "north" : "top",
42572         "south" : "bottom"
42573     },
42574
42575     sanchors : {
42576         "west" : "l",
42577         "east" : "r",
42578         "north" : "t",
42579         "south" : "b"
42580     },
42581
42582     canchors : {
42583         "west" : "tl-tr",
42584         "east" : "tr-tl",
42585         "north" : "tl-bl",
42586         "south" : "bl-tl"
42587     },
42588
42589     getAnchor : function(){
42590         return this.anchors[this.position];
42591     },
42592
42593     getCollapseAnchor : function(){
42594         return this.canchors[this.position];
42595     },
42596
42597     getSlideAnchor : function(){
42598         return this.sanchors[this.position];
42599     },
42600
42601     getAlignAdj : function(){
42602         var cm = this.cmargins;
42603         switch(this.position){
42604             case "west":
42605                 return [0, 0];
42606             break;
42607             case "east":
42608                 return [0, 0];
42609             break;
42610             case "north":
42611                 return [0, 0];
42612             break;
42613             case "south":
42614                 return [0, 0];
42615             break;
42616         }
42617     },
42618
42619     getExpandAdj : function(){
42620         var c = this.collapsedEl, cm = this.cmargins;
42621         switch(this.position){
42622             case "west":
42623                 return [-(cm.right+c.getWidth()+cm.left), 0];
42624             break;
42625             case "east":
42626                 return [cm.right+c.getWidth()+cm.left, 0];
42627             break;
42628             case "north":
42629                 return [0, -(cm.top+cm.bottom+c.getHeight())];
42630             break;
42631             case "south":
42632                 return [0, cm.top+cm.bottom+c.getHeight()];
42633             break;
42634         }
42635     }
42636 });/*
42637  * Based on:
42638  * Ext JS Library 1.1.1
42639  * Copyright(c) 2006-2007, Ext JS, LLC.
42640  *
42641  * Originally Released Under LGPL - original licence link has changed is not relivant.
42642  *
42643  * Fork - LGPL
42644  * <script type="text/javascript">
42645  */
42646 /*
42647  * These classes are private internal classes
42648  */
42649 Roo.bootstrap.layout.Center = function(config){
42650     config.region = "center";
42651     Roo.bootstrap.layout.Region.call(this, config);
42652     this.visible = true;
42653     this.minWidth = config.minWidth || 20;
42654     this.minHeight = config.minHeight || 20;
42655 };
42656
42657 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
42658     hide : function(){
42659         // center panel can't be hidden
42660     },
42661     
42662     show : function(){
42663         // center panel can't be hidden
42664     },
42665     
42666     getMinWidth: function(){
42667         return this.minWidth;
42668     },
42669     
42670     getMinHeight: function(){
42671         return this.minHeight;
42672     }
42673 });
42674
42675
42676
42677
42678  
42679
42680
42681
42682
42683
42684
42685 Roo.bootstrap.layout.North = function(config)
42686 {
42687     config.region = 'north';
42688     config.cursor = 'n-resize';
42689     
42690     Roo.bootstrap.layout.Split.call(this, config);
42691     
42692     
42693     if(this.split){
42694         this.split.placement = Roo.bootstrap.SplitBar.TOP;
42695         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42696         this.split.el.addClass("roo-layout-split-v");
42697     }
42698     //var size = config.initialSize || config.height;
42699     //if(this.el && typeof size != "undefined"){
42700     //    this.el.setHeight(size);
42701     //}
42702 };
42703 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
42704 {
42705     orientation: Roo.bootstrap.SplitBar.VERTICAL,
42706      
42707      
42708     onRender : function(ctr, pos)
42709     {
42710         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42711         var size = this.config.initialSize || this.config.height;
42712         if(this.el && typeof size != "undefined"){
42713             this.el.setHeight(size);
42714         }
42715     
42716     },
42717     
42718     getBox : function(){
42719         if(this.collapsed){
42720             return this.collapsedEl.getBox();
42721         }
42722         var box = this.el.getBox();
42723         if(this.split){
42724             box.height += this.split.el.getHeight();
42725         }
42726         return box;
42727     },
42728     
42729     updateBox : function(box){
42730         if(this.split && !this.collapsed){
42731             box.height -= this.split.el.getHeight();
42732             this.split.el.setLeft(box.x);
42733             this.split.el.setTop(box.y+box.height);
42734             this.split.el.setWidth(box.width);
42735         }
42736         if(this.collapsed){
42737             this.updateBody(box.width, null);
42738         }
42739         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42740     }
42741 });
42742
42743
42744
42745
42746
42747 Roo.bootstrap.layout.South = function(config){
42748     config.region = 'south';
42749     config.cursor = 's-resize';
42750     Roo.bootstrap.layout.Split.call(this, config);
42751     if(this.split){
42752         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
42753         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42754         this.split.el.addClass("roo-layout-split-v");
42755     }
42756     
42757 };
42758
42759 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
42760     orientation: Roo.bootstrap.SplitBar.VERTICAL,
42761     
42762     onRender : function(ctr, pos)
42763     {
42764         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42765         var size = this.config.initialSize || this.config.height;
42766         if(this.el && typeof size != "undefined"){
42767             this.el.setHeight(size);
42768         }
42769     
42770     },
42771     
42772     getBox : function(){
42773         if(this.collapsed){
42774             return this.collapsedEl.getBox();
42775         }
42776         var box = this.el.getBox();
42777         if(this.split){
42778             var sh = this.split.el.getHeight();
42779             box.height += sh;
42780             box.y -= sh;
42781         }
42782         return box;
42783     },
42784     
42785     updateBox : function(box){
42786         if(this.split && !this.collapsed){
42787             var sh = this.split.el.getHeight();
42788             box.height -= sh;
42789             box.y += sh;
42790             this.split.el.setLeft(box.x);
42791             this.split.el.setTop(box.y-sh);
42792             this.split.el.setWidth(box.width);
42793         }
42794         if(this.collapsed){
42795             this.updateBody(box.width, null);
42796         }
42797         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42798     }
42799 });
42800
42801 Roo.bootstrap.layout.East = function(config){
42802     config.region = "east";
42803     config.cursor = "e-resize";
42804     Roo.bootstrap.layout.Split.call(this, config);
42805     if(this.split){
42806         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
42807         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
42808         this.split.el.addClass("roo-layout-split-h");
42809     }
42810     
42811 };
42812 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
42813     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
42814     
42815     onRender : function(ctr, pos)
42816     {
42817         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42818         var size = this.config.initialSize || this.config.width;
42819         if(this.el && typeof size != "undefined"){
42820             this.el.setWidth(size);
42821         }
42822     
42823     },
42824     
42825     getBox : function(){
42826         if(this.collapsed){
42827             return this.collapsedEl.getBox();
42828         }
42829         var box = this.el.getBox();
42830         if(this.split){
42831             var sw = this.split.el.getWidth();
42832             box.width += sw;
42833             box.x -= sw;
42834         }
42835         return box;
42836     },
42837
42838     updateBox : function(box){
42839         if(this.split && !this.collapsed){
42840             var sw = this.split.el.getWidth();
42841             box.width -= sw;
42842             this.split.el.setLeft(box.x);
42843             this.split.el.setTop(box.y);
42844             this.split.el.setHeight(box.height);
42845             box.x += sw;
42846         }
42847         if(this.collapsed){
42848             this.updateBody(null, box.height);
42849         }
42850         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42851     }
42852 });
42853
42854 Roo.bootstrap.layout.West = function(config){
42855     config.region = "west";
42856     config.cursor = "w-resize";
42857     
42858     Roo.bootstrap.layout.Split.call(this, config);
42859     if(this.split){
42860         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
42861         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
42862         this.split.el.addClass("roo-layout-split-h");
42863     }
42864     
42865 };
42866 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
42867     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
42868     
42869     onRender: function(ctr, pos)
42870     {
42871         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
42872         var size = this.config.initialSize || this.config.width;
42873         if(typeof size != "undefined"){
42874             this.el.setWidth(size);
42875         }
42876     },
42877     
42878     getBox : function(){
42879         if(this.collapsed){
42880             return this.collapsedEl.getBox();
42881         }
42882         var box = this.el.getBox();
42883         if (box.width == 0) {
42884             box.width = this.config.width; // kludge?
42885         }
42886         if(this.split){
42887             box.width += this.split.el.getWidth();
42888         }
42889         return box;
42890     },
42891     
42892     updateBox : function(box){
42893         if(this.split && !this.collapsed){
42894             var sw = this.split.el.getWidth();
42895             box.width -= sw;
42896             this.split.el.setLeft(box.x+box.width);
42897             this.split.el.setTop(box.y);
42898             this.split.el.setHeight(box.height);
42899         }
42900         if(this.collapsed){
42901             this.updateBody(null, box.height);
42902         }
42903         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42904     }
42905 });/*
42906  * Based on:
42907  * Ext JS Library 1.1.1
42908  * Copyright(c) 2006-2007, Ext JS, LLC.
42909  *
42910  * Originally Released Under LGPL - original licence link has changed is not relivant.
42911  *
42912  * Fork - LGPL
42913  * <script type="text/javascript">
42914  */
42915 /**
42916  * @class Roo.bootstrap.paenl.Content
42917  * @extends Roo.util.Observable
42918  * @children Roo.bootstrap.Component
42919  * @parent builder Roo.bootstrap.layout.Border
42920  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
42921  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
42922  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
42923  * @cfg {Boolean/Object} autoCreate True to auto generate the DOM element for this panel, or a {@link Roo.DomHelper} config of the element to create
42924  * @cfg {Boolean}   closable      True if the panel can be closed/removed
42925  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
42926  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
42927  * @cfg {Toolbar}   toolbar       A toolbar for this panel
42928  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
42929  * @cfg {String} title          The title for this panel
42930  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
42931  * @cfg {String} url            Calls {@link #setUrl} with this value
42932  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
42933  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
42934  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
42935  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
42936  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
42937  * @cfg {Boolean} badges render the badges
42938  * @cfg {String} cls  extra classes to use  
42939  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
42940  
42941  * @constructor
42942  * Create a new ContentPanel.
42943  * @param {String/Object} config A string to set only the title or a config object
42944  
42945  */
42946 Roo.bootstrap.panel.Content = function( config){
42947     
42948     this.tpl = config.tpl || false;
42949     
42950     var el = config.el;
42951     var content = config.content;
42952
42953     if(config.autoCreate){ // xtype is available if this is called from factory
42954         el = Roo.id();
42955     }
42956     this.el = Roo.get(el);
42957     if(!this.el && config && config.autoCreate){
42958         if(typeof config.autoCreate == "object"){
42959             if(!config.autoCreate.id){
42960                 config.autoCreate.id = config.id||el;
42961             }
42962             this.el = Roo.DomHelper.append(document.body,
42963                         config.autoCreate, true);
42964         }else{
42965             var elcfg =  {
42966                 tag: "div",
42967                 cls: (config.cls || '') +
42968                     (config.background ? ' bg-' + config.background : '') +
42969                     " roo-layout-inactive-content",
42970                 id: config.id||el
42971             };
42972             if (config.iframe) {
42973                 elcfg.cn = [
42974                     {
42975                         tag : 'iframe',
42976                         style : 'border: 0px',
42977                         src : 'about:blank'
42978                     }
42979                 ];
42980             }
42981               
42982             if (config.html) {
42983                 elcfg.html = config.html;
42984                 
42985             }
42986                         
42987             this.el = Roo.DomHelper.append(document.body, elcfg , true);
42988             if (config.iframe) {
42989                 this.iframeEl = this.el.select('iframe',true).first();
42990             }
42991             
42992         }
42993     } 
42994     this.closable = false;
42995     this.loaded = false;
42996     this.active = false;
42997    
42998       
42999     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
43000         
43001         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
43002         
43003         this.wrapEl = this.el; //this.el.wrap();
43004         var ti = [];
43005         if (config.toolbar.items) {
43006             ti = config.toolbar.items ;
43007             delete config.toolbar.items ;
43008         }
43009         
43010         var nitems = [];
43011         this.toolbar.render(this.wrapEl, 'before');
43012         for(var i =0;i < ti.length;i++) {
43013           //  Roo.log(['add child', items[i]]);
43014             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43015         }
43016         this.toolbar.items = nitems;
43017         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
43018         delete config.toolbar;
43019         
43020     }
43021     /*
43022     // xtype created footer. - not sure if will work as we normally have to render first..
43023     if (this.footer && !this.footer.el && this.footer.xtype) {
43024         if (!this.wrapEl) {
43025             this.wrapEl = this.el.wrap();
43026         }
43027     
43028         this.footer.container = this.wrapEl.createChild();
43029          
43030         this.footer = Roo.factory(this.footer, Roo);
43031         
43032     }
43033     */
43034     
43035      if(typeof config == "string"){
43036         this.title = config;
43037     }else{
43038         Roo.apply(this, config);
43039     }
43040     
43041     if(this.resizeEl){
43042         this.resizeEl = Roo.get(this.resizeEl, true);
43043     }else{
43044         this.resizeEl = this.el;
43045     }
43046     // handle view.xtype
43047     
43048  
43049     
43050     
43051     this.addEvents({
43052         /**
43053          * @event activate
43054          * Fires when this panel is activated. 
43055          * @param {Roo.ContentPanel} this
43056          */
43057         "activate" : true,
43058         /**
43059          * @event deactivate
43060          * Fires when this panel is activated. 
43061          * @param {Roo.ContentPanel} this
43062          */
43063         "deactivate" : true,
43064
43065         /**
43066          * @event resize
43067          * Fires when this panel is resized if fitToFrame is true.
43068          * @param {Roo.ContentPanel} this
43069          * @param {Number} width The width after any component adjustments
43070          * @param {Number} height The height after any component adjustments
43071          */
43072         "resize" : true,
43073         
43074          /**
43075          * @event render
43076          * Fires when this tab is created
43077          * @param {Roo.ContentPanel} this
43078          */
43079         "render" : true,
43080         
43081           /**
43082          * @event scroll
43083          * Fires when this content is scrolled
43084          * @param {Roo.ContentPanel} this
43085          * @param {Event} scrollEvent
43086          */
43087         "scroll" : true
43088         
43089         
43090         
43091     });
43092     
43093
43094     
43095     
43096     if(this.autoScroll && !this.iframe){
43097         this.resizeEl.setStyle("overflow", "auto");
43098         this.resizeEl.on('scroll', this.onScroll, this);
43099     } else {
43100         // fix randome scrolling
43101         //this.el.on('scroll', function() {
43102         //    Roo.log('fix random scolling');
43103         //    this.scrollTo('top',0); 
43104         //});
43105     }
43106     content = content || this.content;
43107     if(content){
43108         this.setContent(content);
43109     }
43110     if(config && config.url){
43111         this.setUrl(this.url, this.params, this.loadOnce);
43112     }
43113     
43114     
43115     
43116     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
43117     
43118     if (this.view && typeof(this.view.xtype) != 'undefined') {
43119         this.view.el = this.el.appendChild(document.createElement("div"));
43120         this.view = Roo.factory(this.view); 
43121         this.view.render  &&  this.view.render(false, '');  
43122     }
43123     
43124     
43125     this.fireEvent('render', this);
43126 };
43127
43128 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
43129     
43130     cls : '',
43131     background : '',
43132     
43133     tabTip : '',
43134     
43135     iframe : false,
43136     iframeEl : false,
43137     
43138     /* Resize Element - use this to work out scroll etc. */
43139     resizeEl : false,
43140     
43141     setRegion : function(region){
43142         this.region = region;
43143         this.setActiveClass(region && !this.background);
43144     },
43145     
43146     
43147     setActiveClass: function(state)
43148     {
43149         if(state){
43150            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
43151            this.el.setStyle('position','relative');
43152         }else{
43153            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
43154            this.el.setStyle('position', 'absolute');
43155         } 
43156     },
43157     
43158     /**
43159      * Returns the toolbar for this Panel if one was configured. 
43160      * @return {Roo.Toolbar} 
43161      */
43162     getToolbar : function(){
43163         return this.toolbar;
43164     },
43165     
43166     setActiveState : function(active)
43167     {
43168         this.active = active;
43169         this.setActiveClass(active);
43170         if(!active){
43171             if(this.fireEvent("deactivate", this) === false){
43172                 return false;
43173             }
43174             return true;
43175         }
43176         this.fireEvent("activate", this);
43177         return true;
43178     },
43179     /**
43180      * Updates this panel's element (not for iframe)
43181      * @param {String} content The new content
43182      * @param {Boolean} loadScripts (optional) true to look for and process scripts
43183     */
43184     setContent : function(content, loadScripts){
43185         if (this.iframe) {
43186             return;
43187         }
43188         
43189         this.el.update(content, loadScripts);
43190     },
43191
43192     ignoreResize : function(w, h)
43193     {
43194         //return false; // always resize?
43195         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
43196             return true;
43197         }else{
43198             this.lastSize = {width: w, height: h};
43199             return false;
43200         }
43201     },
43202     /**
43203      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
43204      * @return {Roo.UpdateManager} The UpdateManager
43205      */
43206     getUpdateManager : function(){
43207         if (this.iframe) {
43208             return false;
43209         }
43210         return this.el.getUpdateManager();
43211     },
43212      /**
43213      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
43214      * Does not work with IFRAME contents
43215      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
43216 <pre><code>
43217 panel.load({
43218     url: "your-url.php",
43219     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
43220     callback: yourFunction,
43221     scope: yourObject, //(optional scope)
43222     discardUrl: false,
43223     nocache: false,
43224     text: "Loading...",
43225     timeout: 30,
43226     scripts: false
43227 });
43228 </code></pre>
43229      
43230      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
43231      * are shorthand for <i>disableCaching</i>, <i>indicatorText</i> and <i>loadScripts</i> and are used to set their associated property on this panel UpdateManager instance.
43232      * @param {String/Object} params (optional) The parameters to pass as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
43233      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
43234      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
43235      * @return {Roo.ContentPanel} this
43236      */
43237     load : function(){
43238         
43239         if (this.iframe) {
43240             return this;
43241         }
43242         
43243         var um = this.el.getUpdateManager();
43244         um.update.apply(um, arguments);
43245         return this;
43246     },
43247
43248
43249     /**
43250      * Set a URL to be used to load the content for this panel. When this panel is activated, the content will be loaded from that URL.
43251      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
43252      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
43253      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this panel is activated. (Defaults to false)
43254      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
43255      */
43256     setUrl : function(url, params, loadOnce){
43257         if (this.iframe) {
43258             this.iframeEl.dom.src = url;
43259             return false;
43260         }
43261         
43262         if(this.refreshDelegate){
43263             this.removeListener("activate", this.refreshDelegate);
43264         }
43265         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
43266         this.on("activate", this.refreshDelegate);
43267         return this.el.getUpdateManager();
43268     },
43269     
43270     _handleRefresh : function(url, params, loadOnce){
43271         if(!loadOnce || !this.loaded){
43272             var updater = this.el.getUpdateManager();
43273             updater.update(url, params, this._setLoaded.createDelegate(this));
43274         }
43275     },
43276     
43277     _setLoaded : function(){
43278         this.loaded = true;
43279     }, 
43280     
43281     /**
43282      * Returns this panel's id
43283      * @return {String} 
43284      */
43285     getId : function(){
43286         return this.el.id;
43287     },
43288     
43289     /** 
43290      * Returns this panel's element - used by regiosn to add.
43291      * @return {Roo.Element} 
43292      */
43293     getEl : function(){
43294         return this.wrapEl || this.el;
43295     },
43296     
43297    
43298     
43299     adjustForComponents : function(width, height)
43300     {
43301         //Roo.log('adjustForComponents ');
43302         if(this.resizeEl != this.el){
43303             width -= this.el.getFrameWidth('lr');
43304             height -= this.el.getFrameWidth('tb');
43305         }
43306         if(this.toolbar){
43307             var te = this.toolbar.getEl();
43308             te.setWidth(width);
43309             height -= te.getHeight();
43310         }
43311         if(this.footer){
43312             var te = this.footer.getEl();
43313             te.setWidth(width);
43314             height -= te.getHeight();
43315         }
43316         
43317         
43318         if(this.adjustments){
43319             width += this.adjustments[0];
43320             height += this.adjustments[1];
43321         }
43322         return {"width": width, "height": height};
43323     },
43324     
43325     setSize : function(width, height){
43326         if(this.fitToFrame && !this.ignoreResize(width, height)){
43327             if(this.fitContainer && this.resizeEl != this.el){
43328                 this.el.setSize(width, height);
43329             }
43330             var size = this.adjustForComponents(width, height);
43331             if (this.iframe) {
43332                 this.iframeEl.setSize(width,height);
43333             }
43334             
43335             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
43336             this.fireEvent('resize', this, size.width, size.height);
43337             
43338             
43339         }
43340     },
43341     
43342     /**
43343      * Returns this panel's title
43344      * @return {String} 
43345      */
43346     getTitle : function(){
43347         
43348         if (typeof(this.title) != 'object') {
43349             return this.title;
43350         }
43351         
43352         var t = '';
43353         for (var k in this.title) {
43354             if (!this.title.hasOwnProperty(k)) {
43355                 continue;
43356             }
43357             
43358             if (k.indexOf('-') >= 0) {
43359                 var s = k.split('-');
43360                 for (var i = 0; i<s.length; i++) {
43361                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
43362                 }
43363             } else {
43364                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
43365             }
43366         }
43367         return t;
43368     },
43369     
43370     /**
43371      * Set this panel's title
43372      * @param {String} title
43373      */
43374     setTitle : function(title){
43375         this.title = title;
43376         if(this.region){
43377             this.region.updatePanelTitle(this, title);
43378         }
43379     },
43380     
43381     /**
43382      * Returns true is this panel was configured to be closable
43383      * @return {Boolean} 
43384      */
43385     isClosable : function(){
43386         return this.closable;
43387     },
43388     
43389     beforeSlide : function(){
43390         this.el.clip();
43391         this.resizeEl.clip();
43392     },
43393     
43394     afterSlide : function(){
43395         this.el.unclip();
43396         this.resizeEl.unclip();
43397     },
43398     
43399     /**
43400      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
43401      *   Will fail silently if the {@link #setUrl} method has not been called.
43402      *   This does not activate the panel, just updates its content.
43403      */
43404     refresh : function(){
43405         if(this.refreshDelegate){
43406            this.loaded = false;
43407            this.refreshDelegate();
43408         }
43409     },
43410     
43411     /**
43412      * Destroys this panel
43413      */
43414     destroy : function(){
43415         this.el.removeAllListeners();
43416         var tempEl = document.createElement("span");
43417         tempEl.appendChild(this.el.dom);
43418         tempEl.innerHTML = "";
43419         this.el.remove();
43420         this.el = null;
43421     },
43422     
43423     /**
43424      * form - if the content panel contains a form - this is a reference to it.
43425      * @type {Roo.form.Form}
43426      */
43427     form : false,
43428     /**
43429      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
43430      *    This contains a reference to it.
43431      * @type {Roo.View}
43432      */
43433     view : false,
43434     
43435       /**
43436      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
43437      * <pre><code>
43438
43439 layout.addxtype({
43440        xtype : 'Form',
43441        items: [ .... ]
43442    }
43443 );
43444
43445 </code></pre>
43446      * @param {Object} cfg Xtype definition of item to add.
43447      */
43448     
43449     
43450     getChildContainer: function () {
43451         return this.getEl();
43452     },
43453     
43454     
43455     onScroll : function(e)
43456     {
43457         this.fireEvent('scroll', this, e);
43458     }
43459     
43460     
43461     /*
43462         var  ret = new Roo.factory(cfg);
43463         return ret;
43464         
43465         
43466         // add form..
43467         if (cfg.xtype.match(/^Form$/)) {
43468             
43469             var el;
43470             //if (this.footer) {
43471             //    el = this.footer.container.insertSibling(false, 'before');
43472             //} else {
43473                 el = this.el.createChild();
43474             //}
43475
43476             this.form = new  Roo.form.Form(cfg);
43477             
43478             
43479             if ( this.form.allItems.length) {
43480                 this.form.render(el.dom);
43481             }
43482             return this.form;
43483         }
43484         // should only have one of theses..
43485         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
43486             // views.. should not be just added - used named prop 'view''
43487             
43488             cfg.el = this.el.appendChild(document.createElement("div"));
43489             // factory?
43490             
43491             var ret = new Roo.factory(cfg);
43492              
43493              ret.render && ret.render(false, ''); // render blank..
43494             this.view = ret;
43495             return ret;
43496         }
43497         return false;
43498     }
43499     \*/
43500 });
43501  
43502 /**
43503  * @class Roo.bootstrap.panel.Grid
43504  * @extends Roo.bootstrap.panel.Content
43505  * @constructor
43506  * Create a new GridPanel.
43507  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
43508  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
43509  * @param {Object} config A the config object
43510   
43511  */
43512
43513
43514
43515 Roo.bootstrap.panel.Grid = function(config)
43516 {
43517     
43518       
43519     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
43520         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
43521
43522     config.el = this.wrapper;
43523     //this.el = this.wrapper;
43524     
43525       if (config.container) {
43526         // ctor'ed from a Border/panel.grid
43527         
43528         
43529         this.wrapper.setStyle("overflow", "hidden");
43530         this.wrapper.addClass('roo-grid-container');
43531
43532     }
43533     
43534     
43535     if(config.toolbar){
43536         var tool_el = this.wrapper.createChild();    
43537         this.toolbar = Roo.factory(config.toolbar);
43538         var ti = [];
43539         if (config.toolbar.items) {
43540             ti = config.toolbar.items ;
43541             delete config.toolbar.items ;
43542         }
43543         
43544         var nitems = [];
43545         this.toolbar.render(tool_el);
43546         for(var i =0;i < ti.length;i++) {
43547           //  Roo.log(['add child', items[i]]);
43548             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43549         }
43550         this.toolbar.items = nitems;
43551         
43552         delete config.toolbar;
43553     }
43554     
43555     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
43556     config.grid.scrollBody = true;;
43557     config.grid.monitorWindowResize = false; // turn off autosizing
43558     config.grid.autoHeight = false;
43559     config.grid.autoWidth = false;
43560     
43561     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
43562     
43563     if (config.background) {
43564         // render grid on panel activation (if panel background)
43565         this.on('activate', function(gp) {
43566             if (!gp.grid.rendered) {
43567                 gp.grid.render(this.wrapper);
43568                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
43569             }
43570         });
43571             
43572     } else {
43573         this.grid.render(this.wrapper);
43574         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
43575
43576     }
43577     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
43578     // ??? needed ??? config.el = this.wrapper;
43579     
43580     
43581     
43582   
43583     // xtype created footer. - not sure if will work as we normally have to render first..
43584     if (this.footer && !this.footer.el && this.footer.xtype) {
43585         
43586         var ctr = this.grid.getView().getFooterPanel(true);
43587         this.footer.dataSource = this.grid.dataSource;
43588         this.footer = Roo.factory(this.footer, Roo);
43589         this.footer.render(ctr);
43590         
43591     }
43592     
43593     
43594     
43595     
43596      
43597 };
43598
43599 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
43600 {
43601   
43602     getId : function(){
43603         return this.grid.id;
43604     },
43605     
43606     /**
43607      * Returns the grid for this panel
43608      * @return {Roo.bootstrap.Table} 
43609      */
43610     getGrid : function(){
43611         return this.grid;    
43612     },
43613     
43614     setSize : function(width, height)
43615     {
43616      
43617         //if(!this.ignoreResize(width, height)){
43618             var grid = this.grid;
43619             var size = this.adjustForComponents(width, height);
43620             // tfoot is not a footer?
43621           
43622             
43623             var gridel = grid.getGridEl();
43624             gridel.setSize(size.width, size.height);
43625             
43626             var tbd = grid.getGridEl().select('tbody', true).first();
43627             var thd = grid.getGridEl().select('thead',true).first();
43628             var tbf= grid.getGridEl().select('tfoot', true).first();
43629
43630             if (tbf) {
43631                 size.height -= tbf.getHeight();
43632             }
43633             if (thd) {
43634                 size.height -= thd.getHeight();
43635             }
43636             
43637             tbd.setSize(size.width, size.height );
43638             // this is for the account management tab -seems to work there.
43639             var thd = grid.getGridEl().select('thead',true).first();
43640             //if (tbd) {
43641             //    tbd.setSize(size.width, size.height - thd.getHeight());
43642             //}
43643              
43644             grid.autoSize();
43645         //}
43646    
43647     },
43648      
43649     
43650     
43651     beforeSlide : function(){
43652         this.grid.getView().scroller.clip();
43653     },
43654     
43655     afterSlide : function(){
43656         this.grid.getView().scroller.unclip();
43657     },
43658     
43659     destroy : function(){
43660         this.grid.destroy();
43661         delete this.grid;
43662         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
43663     }
43664 });
43665
43666 /**
43667  * @class Roo.bootstrap.panel.Nest
43668  * @extends Roo.bootstrap.panel.Content
43669  * @constructor
43670  * Create a new Panel, that can contain a layout.Border.
43671  * 
43672  * 
43673  * @param {String/Object} config A string to set only the title or a config object
43674  */
43675 Roo.bootstrap.panel.Nest = function(config)
43676 {
43677     // construct with only one argument..
43678     /* FIXME - implement nicer consturctors
43679     if (layout.layout) {
43680         config = layout;
43681         layout = config.layout;
43682         delete config.layout;
43683     }
43684     if (layout.xtype && !layout.getEl) {
43685         // then layout needs constructing..
43686         layout = Roo.factory(layout, Roo);
43687     }
43688     */
43689     
43690     config.el =  config.layout.getEl();
43691     
43692     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
43693     
43694     config.layout.monitorWindowResize = false; // turn off autosizing
43695     this.layout = config.layout;
43696     this.layout.getEl().addClass("roo-layout-nested-layout");
43697     this.layout.parent = this;
43698     
43699     
43700     
43701     
43702 };
43703
43704 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
43705     /**
43706     * @cfg {Roo.BorderLayout} layout The layout for this panel
43707     */
43708     layout : false,
43709
43710     setSize : function(width, height){
43711         if(!this.ignoreResize(width, height)){
43712             var size = this.adjustForComponents(width, height);
43713             var el = this.layout.getEl();
43714             if (size.height < 1) {
43715                 el.setWidth(size.width);   
43716             } else {
43717                 el.setSize(size.width, size.height);
43718             }
43719             var touch = el.dom.offsetWidth;
43720             this.layout.layout();
43721             // ie requires a double layout on the first pass
43722             if(Roo.isIE && !this.initialized){
43723                 this.initialized = true;
43724                 this.layout.layout();
43725             }
43726         }
43727     },
43728     
43729     // activate all subpanels if not currently active..
43730     
43731     setActiveState : function(active){
43732         this.active = active;
43733         this.setActiveClass(active);
43734         
43735         if(!active){
43736             this.fireEvent("deactivate", this);
43737             return;
43738         }
43739         
43740         this.fireEvent("activate", this);
43741         // not sure if this should happen before or after..
43742         if (!this.layout) {
43743             return; // should not happen..
43744         }
43745         var reg = false;
43746         for (var r in this.layout.regions) {
43747             reg = this.layout.getRegion(r);
43748             if (reg.getActivePanel()) {
43749                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
43750                 reg.setActivePanel(reg.getActivePanel());
43751                 continue;
43752             }
43753             if (!reg.panels.length) {
43754                 continue;
43755             }
43756             reg.showPanel(reg.getPanel(0));
43757         }
43758         
43759         
43760         
43761         
43762     },
43763     
43764     /**
43765      * Returns the nested BorderLayout for this panel
43766      * @return {Roo.BorderLayout} 
43767      */
43768     getLayout : function(){
43769         return this.layout;
43770     },
43771     
43772      /**
43773      * Adds a xtype elements to the layout of the nested panel
43774      * <pre><code>
43775
43776 panel.addxtype({
43777        xtype : 'ContentPanel',
43778        region: 'west',
43779        items: [ .... ]
43780    }
43781 );
43782
43783 panel.addxtype({
43784         xtype : 'NestedLayoutPanel',
43785         region: 'west',
43786         layout: {
43787            center: { },
43788            west: { }   
43789         },
43790         items : [ ... list of content panels or nested layout panels.. ]
43791    }
43792 );
43793 </code></pre>
43794      * @param {Object} cfg Xtype definition of item to add.
43795      */
43796     addxtype : function(cfg) {
43797         return this.layout.addxtype(cfg);
43798     
43799     }
43800 });/*
43801  * Based on:
43802  * Ext JS Library 1.1.1
43803  * Copyright(c) 2006-2007, Ext JS, LLC.
43804  *
43805  * Originally Released Under LGPL - original licence link has changed is not relivant.
43806  *
43807  * Fork - LGPL
43808  * <script type="text/javascript">
43809  */
43810 /**
43811  * @class Roo.TabPanel
43812  * @extends Roo.util.Observable
43813  * A lightweight tab container.
43814  * <br><br>
43815  * Usage:
43816  * <pre><code>
43817 // basic tabs 1, built from existing content
43818 var tabs = new Roo.TabPanel("tabs1");
43819 tabs.addTab("script", "View Script");
43820 tabs.addTab("markup", "View Markup");
43821 tabs.activate("script");
43822
43823 // more advanced tabs, built from javascript
43824 var jtabs = new Roo.TabPanel("jtabs");
43825 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
43826
43827 // set up the UpdateManager
43828 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
43829 var updater = tab2.getUpdateManager();
43830 updater.setDefaultUrl("ajax1.htm");
43831 tab2.on('activate', updater.refresh, updater, true);
43832
43833 // Use setUrl for Ajax loading
43834 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
43835 tab3.setUrl("ajax2.htm", null, true);
43836
43837 // Disabled tab
43838 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
43839 tab4.disable();
43840
43841 jtabs.activate("jtabs-1");
43842  * </code></pre>
43843  * @constructor
43844  * Create a new TabPanel.
43845  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
43846  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
43847  */
43848 Roo.bootstrap.panel.Tabs = function(config){
43849     /**
43850     * The container element for this TabPanel.
43851     * @type Roo.Element
43852     */
43853     this.el = Roo.get(config.el);
43854     delete config.el;
43855     if(config){
43856         if(typeof config == "boolean"){
43857             this.tabPosition = config ? "bottom" : "top";
43858         }else{
43859             Roo.apply(this, config);
43860         }
43861     }
43862     
43863     if(this.tabPosition == "bottom"){
43864         // if tabs are at the bottom = create the body first.
43865         this.bodyEl = Roo.get(this.createBody(this.el.dom));
43866         this.el.addClass("roo-tabs-bottom");
43867     }
43868     // next create the tabs holders
43869     
43870     if (this.tabPosition == "west"){
43871         
43872         var reg = this.region; // fake it..
43873         while (reg) {
43874             if (!reg.mgr.parent) {
43875                 break;
43876             }
43877             reg = reg.mgr.parent.region;
43878         }
43879         Roo.log("got nest?");
43880         Roo.log(reg);
43881         if (reg.mgr.getRegion('west')) {
43882             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
43883             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
43884             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
43885             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
43886             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
43887         
43888             
43889         }
43890         
43891         
43892     } else {
43893      
43894         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
43895         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
43896         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
43897         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
43898     }
43899     
43900     
43901     if(Roo.isIE){
43902         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
43903     }
43904     
43905     // finally - if tabs are at the top, then create the body last..
43906     if(this.tabPosition != "bottom"){
43907         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
43908          * @type Roo.Element
43909          */
43910         this.bodyEl = Roo.get(this.createBody(this.el.dom));
43911         this.el.addClass("roo-tabs-top");
43912     }
43913     this.items = [];
43914
43915     this.bodyEl.setStyle("position", "relative");
43916
43917     this.active = null;
43918     this.activateDelegate = this.activate.createDelegate(this);
43919
43920     this.addEvents({
43921         /**
43922          * @event tabchange
43923          * Fires when the active tab changes
43924          * @param {Roo.TabPanel} this
43925          * @param {Roo.TabPanelItem} activePanel The new active tab
43926          */
43927         "tabchange": true,
43928         /**
43929          * @event beforetabchange
43930          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
43931          * @param {Roo.TabPanel} this
43932          * @param {Object} e Set cancel to true on this object to cancel the tab change
43933          * @param {Roo.TabPanelItem} tab The tab being changed to
43934          */
43935         "beforetabchange" : true
43936     });
43937
43938     Roo.EventManager.onWindowResize(this.onResize, this);
43939     this.cpad = this.el.getPadding("lr");
43940     this.hiddenCount = 0;
43941
43942
43943     // toolbar on the tabbar support...
43944     if (this.toolbar) {
43945         alert("no toolbar support yet");
43946         this.toolbar  = false;
43947         /*
43948         var tcfg = this.toolbar;
43949         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
43950         this.toolbar = new Roo.Toolbar(tcfg);
43951         if (Roo.isSafari) {
43952             var tbl = tcfg.container.child('table', true);
43953             tbl.setAttribute('width', '100%');
43954         }
43955         */
43956         
43957     }
43958    
43959
43960
43961     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
43962 };
43963
43964 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
43965     /*
43966      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
43967      */
43968     tabPosition : "top",
43969     /*
43970      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
43971      */
43972     currentTabWidth : 0,
43973     /*
43974      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
43975      */
43976     minTabWidth : 40,
43977     /*
43978      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
43979      */
43980     maxTabWidth : 250,
43981     /*
43982      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
43983      */
43984     preferredTabWidth : 175,
43985     /*
43986      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
43987      */
43988     resizeTabs : false,
43989     /*
43990      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
43991      */
43992     monitorResize : true,
43993     /*
43994      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
43995      */
43996     toolbar : false,  // set by caller..
43997     
43998     region : false, /// set by caller
43999     
44000     disableTooltips : true, // not used yet...
44001
44002     /**
44003      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
44004      * @param {String} id The id of the div to use <b>or create</b>
44005      * @param {String} text The text for the tab
44006      * @param {String} content (optional) Content to put in the TabPanelItem body
44007      * @param {Boolean} closable (optional) True to create a close icon on the tab
44008      * @return {Roo.TabPanelItem} The created TabPanelItem
44009      */
44010     addTab : function(id, text, content, closable, tpl)
44011     {
44012         var item = new Roo.bootstrap.panel.TabItem({
44013             panel: this,
44014             id : id,
44015             text : text,
44016             closable : closable,
44017             tpl : tpl
44018         });
44019         this.addTabItem(item);
44020         if(content){
44021             item.setContent(content);
44022         }
44023         return item;
44024     },
44025
44026     /**
44027      * Returns the {@link Roo.TabPanelItem} with the specified id/index
44028      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
44029      * @return {Roo.TabPanelItem}
44030      */
44031     getTab : function(id){
44032         return this.items[id];
44033     },
44034
44035     /**
44036      * Hides the {@link Roo.TabPanelItem} with the specified id/index
44037      * @param {String/Number} id The id or index of the TabPanelItem to hide.
44038      */
44039     hideTab : function(id){
44040         var t = this.items[id];
44041         if(!t.isHidden()){
44042            t.setHidden(true);
44043            this.hiddenCount++;
44044            this.autoSizeTabs();
44045         }
44046     },
44047
44048     /**
44049      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
44050      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
44051      */
44052     unhideTab : function(id){
44053         var t = this.items[id];
44054         if(t.isHidden()){
44055            t.setHidden(false);
44056            this.hiddenCount--;
44057            this.autoSizeTabs();
44058         }
44059     },
44060
44061     /**
44062      * Adds an existing {@link Roo.TabPanelItem}.
44063      * @param {Roo.TabPanelItem} item The TabPanelItem to add
44064      */
44065     addTabItem : function(item)
44066     {
44067         this.items[item.id] = item;
44068         this.items.push(item);
44069         this.autoSizeTabs();
44070       //  if(this.resizeTabs){
44071     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
44072   //         this.autoSizeTabs();
44073 //        }else{
44074 //            item.autoSize();
44075        // }
44076     },
44077
44078     /**
44079      * Removes a {@link Roo.TabPanelItem}.
44080      * @param {String/Number} id The id or index of the TabPanelItem to remove.
44081      */
44082     removeTab : function(id){
44083         var items = this.items;
44084         var tab = items[id];
44085         if(!tab) { return; }
44086         var index = items.indexOf(tab);
44087         if(this.active == tab && items.length > 1){
44088             var newTab = this.getNextAvailable(index);
44089             if(newTab) {
44090                 newTab.activate();
44091             }
44092         }
44093         this.stripEl.dom.removeChild(tab.pnode.dom);
44094         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
44095             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
44096         }
44097         items.splice(index, 1);
44098         delete this.items[tab.id];
44099         tab.fireEvent("close", tab);
44100         tab.purgeListeners();
44101         this.autoSizeTabs();
44102     },
44103
44104     getNextAvailable : function(start){
44105         var items = this.items;
44106         var index = start;
44107         // look for a next tab that will slide over to
44108         // replace the one being removed
44109         while(index < items.length){
44110             var item = items[++index];
44111             if(item && !item.isHidden()){
44112                 return item;
44113             }
44114         }
44115         // if one isn't found select the previous tab (on the left)
44116         index = start;
44117         while(index >= 0){
44118             var item = items[--index];
44119             if(item && !item.isHidden()){
44120                 return item;
44121             }
44122         }
44123         return null;
44124     },
44125
44126     /**
44127      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
44128      * @param {String/Number} id The id or index of the TabPanelItem to disable.
44129      */
44130     disableTab : function(id){
44131         var tab = this.items[id];
44132         if(tab && this.active != tab){
44133             tab.disable();
44134         }
44135     },
44136
44137     /**
44138      * Enables a {@link Roo.TabPanelItem} that is disabled.
44139      * @param {String/Number} id The id or index of the TabPanelItem to enable.
44140      */
44141     enableTab : function(id){
44142         var tab = this.items[id];
44143         tab.enable();
44144     },
44145
44146     /**
44147      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
44148      * @param {String/Number} id The id or index of the TabPanelItem to activate.
44149      * @return {Roo.TabPanelItem} The TabPanelItem.
44150      */
44151     activate : function(id)
44152     {
44153         //Roo.log('activite:'  + id);
44154         
44155         var tab = this.items[id];
44156         if(!tab){
44157             return null;
44158         }
44159         if(tab == this.active || tab.disabled){
44160             return tab;
44161         }
44162         var e = {};
44163         this.fireEvent("beforetabchange", this, e, tab);
44164         if(e.cancel !== true && !tab.disabled){
44165             if(this.active){
44166                 this.active.hide();
44167             }
44168             this.active = this.items[id];
44169             this.active.show();
44170             this.fireEvent("tabchange", this, this.active);
44171         }
44172         return tab;
44173     },
44174
44175     /**
44176      * Gets the active {@link Roo.TabPanelItem}.
44177      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
44178      */
44179     getActiveTab : function(){
44180         return this.active;
44181     },
44182
44183     /**
44184      * Updates the tab body element to fit the height of the container element
44185      * for overflow scrolling
44186      * @param {Number} targetHeight (optional) Override the starting height from the elements height
44187      */
44188     syncHeight : function(targetHeight){
44189         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44190         var bm = this.bodyEl.getMargins();
44191         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
44192         this.bodyEl.setHeight(newHeight);
44193         return newHeight;
44194     },
44195
44196     onResize : function(){
44197         if(this.monitorResize){
44198             this.autoSizeTabs();
44199         }
44200     },
44201
44202     /**
44203      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
44204      */
44205     beginUpdate : function(){
44206         this.updating = true;
44207     },
44208
44209     /**
44210      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
44211      */
44212     endUpdate : function(){
44213         this.updating = false;
44214         this.autoSizeTabs();
44215     },
44216
44217     /**
44218      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
44219      */
44220     autoSizeTabs : function()
44221     {
44222         var count = this.items.length;
44223         var vcount = count - this.hiddenCount;
44224         
44225         if (vcount < 2) {
44226             this.stripEl.hide();
44227         } else {
44228             this.stripEl.show();
44229         }
44230         
44231         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
44232             return;
44233         }
44234         
44235         
44236         var w = Math.max(this.el.getWidth() - this.cpad, 10);
44237         var availWidth = Math.floor(w / vcount);
44238         var b = this.stripBody;
44239         if(b.getWidth() > w){
44240             var tabs = this.items;
44241             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
44242             if(availWidth < this.minTabWidth){
44243                 /*if(!this.sleft){    // incomplete scrolling code
44244                     this.createScrollButtons();
44245                 }
44246                 this.showScroll();
44247                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
44248             }
44249         }else{
44250             if(this.currentTabWidth < this.preferredTabWidth){
44251                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
44252             }
44253         }
44254     },
44255
44256     /**
44257      * Returns the number of tabs in this TabPanel.
44258      * @return {Number}
44259      */
44260      getCount : function(){
44261          return this.items.length;
44262      },
44263
44264     /**
44265      * Resizes all the tabs to the passed width
44266      * @param {Number} The new width
44267      */
44268     setTabWidth : function(width){
44269         this.currentTabWidth = width;
44270         for(var i = 0, len = this.items.length; i < len; i++) {
44271                 if(!this.items[i].isHidden()) {
44272                 this.items[i].setWidth(width);
44273             }
44274         }
44275     },
44276
44277     /**
44278      * Destroys this TabPanel
44279      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
44280      */
44281     destroy : function(removeEl){
44282         Roo.EventManager.removeResizeListener(this.onResize, this);
44283         for(var i = 0, len = this.items.length; i < len; i++){
44284             this.items[i].purgeListeners();
44285         }
44286         if(removeEl === true){
44287             this.el.update("");
44288             this.el.remove();
44289         }
44290     },
44291     
44292     createStrip : function(container)
44293     {
44294         var strip = document.createElement("nav");
44295         strip.className = Roo.bootstrap.version == 4 ?
44296             "navbar-light bg-light" : 
44297             "navbar navbar-default"; //"x-tabs-wrap";
44298         container.appendChild(strip);
44299         return strip;
44300     },
44301     
44302     createStripList : function(strip)
44303     {
44304         // div wrapper for retard IE
44305         // returns the "tr" element.
44306         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
44307         //'<div class="x-tabs-strip-wrap">'+
44308           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
44309           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
44310         return strip.firstChild; //.firstChild.firstChild.firstChild;
44311     },
44312     createBody : function(container)
44313     {
44314         var body = document.createElement("div");
44315         Roo.id(body, "tab-body");
44316         //Roo.fly(body).addClass("x-tabs-body");
44317         Roo.fly(body).addClass("tab-content");
44318         container.appendChild(body);
44319         return body;
44320     },
44321     createItemBody :function(bodyEl, id){
44322         var body = Roo.getDom(id);
44323         if(!body){
44324             body = document.createElement("div");
44325             body.id = id;
44326         }
44327         //Roo.fly(body).addClass("x-tabs-item-body");
44328         Roo.fly(body).addClass("tab-pane");
44329          bodyEl.insertBefore(body, bodyEl.firstChild);
44330         return body;
44331     },
44332     /** @private */
44333     createStripElements :  function(stripEl, text, closable, tpl)
44334     {
44335         var td = document.createElement("li"); // was td..
44336         td.className = 'nav-item';
44337         
44338         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
44339         
44340         
44341         stripEl.appendChild(td);
44342         /*if(closable){
44343             td.className = "x-tabs-closable";
44344             if(!this.closeTpl){
44345                 this.closeTpl = new Roo.Template(
44346                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44347                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
44348                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
44349                 );
44350             }
44351             var el = this.closeTpl.overwrite(td, {"text": text});
44352             var close = el.getElementsByTagName("div")[0];
44353             var inner = el.getElementsByTagName("em")[0];
44354             return {"el": el, "close": close, "inner": inner};
44355         } else {
44356         */
44357         // not sure what this is..
44358 //            if(!this.tabTpl){
44359                 //this.tabTpl = new Roo.Template(
44360                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44361                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
44362                 //);
44363 //                this.tabTpl = new Roo.Template(
44364 //                   '<a href="#">' +
44365 //                   '<span unselectable="on"' +
44366 //                            (this.disableTooltips ? '' : ' title="{text}"') +
44367 //                            ' >{text}</span></a>'
44368 //                );
44369 //                
44370 //            }
44371
44372
44373             var template = tpl || this.tabTpl || false;
44374             
44375             if(!template){
44376                 template =  new Roo.Template(
44377                         Roo.bootstrap.version == 4 ? 
44378                             (
44379                                 '<a class="nav-link" href="#" unselectable="on"' +
44380                                      (this.disableTooltips ? '' : ' title="{text}"') +
44381                                      ' >{text}</a>'
44382                             ) : (
44383                                 '<a class="nav-link" href="#">' +
44384                                 '<span unselectable="on"' +
44385                                          (this.disableTooltips ? '' : ' title="{text}"') +
44386                                     ' >{text}</span></a>'
44387                             )
44388                 );
44389             }
44390             
44391             switch (typeof(template)) {
44392                 case 'object' :
44393                     break;
44394                 case 'string' :
44395                     template = new Roo.Template(template);
44396                     break;
44397                 default :
44398                     break;
44399             }
44400             
44401             var el = template.overwrite(td, {"text": text});
44402             
44403             var inner = el.getElementsByTagName("span")[0];
44404             
44405             return {"el": el, "inner": inner};
44406             
44407     }
44408         
44409     
44410 });
44411
44412 /**
44413  * @class Roo.TabPanelItem
44414  * @extends Roo.util.Observable
44415  * Represents an individual item (tab plus body) in a TabPanel.
44416  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
44417  * @param {String} id The id of this TabPanelItem
44418  * @param {String} text The text for the tab of this TabPanelItem
44419  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
44420  */
44421 Roo.bootstrap.panel.TabItem = function(config){
44422     /**
44423      * The {@link Roo.TabPanel} this TabPanelItem belongs to
44424      * @type Roo.TabPanel
44425      */
44426     this.tabPanel = config.panel;
44427     /**
44428      * The id for this TabPanelItem
44429      * @type String
44430      */
44431     this.id = config.id;
44432     /** @private */
44433     this.disabled = false;
44434     /** @private */
44435     this.text = config.text;
44436     /** @private */
44437     this.loaded = false;
44438     this.closable = config.closable;
44439
44440     /**
44441      * The body element for this TabPanelItem.
44442      * @type Roo.Element
44443      */
44444     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
44445     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
44446     this.bodyEl.setStyle("display", "block");
44447     this.bodyEl.setStyle("zoom", "1");
44448     //this.hideAction();
44449
44450     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
44451     /** @private */
44452     this.el = Roo.get(els.el);
44453     this.inner = Roo.get(els.inner, true);
44454      this.textEl = Roo.bootstrap.version == 4 ?
44455         this.el : Roo.get(this.el.dom.firstChild, true);
44456
44457     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
44458     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
44459
44460     
44461 //    this.el.on("mousedown", this.onTabMouseDown, this);
44462     this.el.on("click", this.onTabClick, this);
44463     /** @private */
44464     if(config.closable){
44465         var c = Roo.get(els.close, true);
44466         c.dom.title = this.closeText;
44467         c.addClassOnOver("close-over");
44468         c.on("click", this.closeClick, this);
44469      }
44470
44471     this.addEvents({
44472          /**
44473          * @event activate
44474          * Fires when this tab becomes the active tab.
44475          * @param {Roo.TabPanel} tabPanel The parent TabPanel
44476          * @param {Roo.TabPanelItem} this
44477          */
44478         "activate": true,
44479         /**
44480          * @event beforeclose
44481          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
44482          * @param {Roo.TabPanelItem} this
44483          * @param {Object} e Set cancel to true on this object to cancel the close.
44484          */
44485         "beforeclose": true,
44486         /**
44487          * @event close
44488          * Fires when this tab is closed.
44489          * @param {Roo.TabPanelItem} this
44490          */
44491          "close": true,
44492         /**
44493          * @event deactivate
44494          * Fires when this tab is no longer the active tab.
44495          * @param {Roo.TabPanel} tabPanel The parent TabPanel
44496          * @param {Roo.TabPanelItem} this
44497          */
44498          "deactivate" : true
44499     });
44500     this.hidden = false;
44501
44502     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
44503 };
44504
44505 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
44506            {
44507     purgeListeners : function(){
44508        Roo.util.Observable.prototype.purgeListeners.call(this);
44509        this.el.removeAllListeners();
44510     },
44511     /**
44512      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
44513      */
44514     show : function(){
44515         this.status_node.addClass("active");
44516         this.showAction();
44517         if(Roo.isOpera){
44518             this.tabPanel.stripWrap.repaint();
44519         }
44520         this.fireEvent("activate", this.tabPanel, this);
44521     },
44522
44523     /**
44524      * Returns true if this tab is the active tab.
44525      * @return {Boolean}
44526      */
44527     isActive : function(){
44528         return this.tabPanel.getActiveTab() == this;
44529     },
44530
44531     /**
44532      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
44533      */
44534     hide : function(){
44535         this.status_node.removeClass("active");
44536         this.hideAction();
44537         this.fireEvent("deactivate", this.tabPanel, this);
44538     },
44539
44540     hideAction : function(){
44541         this.bodyEl.hide();
44542         this.bodyEl.setStyle("position", "absolute");
44543         this.bodyEl.setLeft("-20000px");
44544         this.bodyEl.setTop("-20000px");
44545     },
44546
44547     showAction : function(){
44548         this.bodyEl.setStyle("position", "relative");
44549         this.bodyEl.setTop("");
44550         this.bodyEl.setLeft("");
44551         this.bodyEl.show();
44552     },
44553
44554     /**
44555      * Set the tooltip for the tab.
44556      * @param {String} tooltip The tab's tooltip
44557      */
44558     setTooltip : function(text){
44559         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
44560             this.textEl.dom.qtip = text;
44561             this.textEl.dom.removeAttribute('title');
44562         }else{
44563             this.textEl.dom.title = text;
44564         }
44565     },
44566
44567     onTabClick : function(e){
44568         e.preventDefault();
44569         this.tabPanel.activate(this.id);
44570     },
44571
44572     onTabMouseDown : function(e){
44573         e.preventDefault();
44574         this.tabPanel.activate(this.id);
44575     },
44576 /*
44577     getWidth : function(){
44578         return this.inner.getWidth();
44579     },
44580
44581     setWidth : function(width){
44582         var iwidth = width - this.linode.getPadding("lr");
44583         this.inner.setWidth(iwidth);
44584         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
44585         this.linode.setWidth(width);
44586     },
44587 */
44588     /**
44589      * Show or hide the tab
44590      * @param {Boolean} hidden True to hide or false to show.
44591      */
44592     setHidden : function(hidden){
44593         this.hidden = hidden;
44594         this.linode.setStyle("display", hidden ? "none" : "");
44595     },
44596
44597     /**
44598      * Returns true if this tab is "hidden"
44599      * @return {Boolean}
44600      */
44601     isHidden : function(){
44602         return this.hidden;
44603     },
44604
44605     /**
44606      * Returns the text for this tab
44607      * @return {String}
44608      */
44609     getText : function(){
44610         return this.text;
44611     },
44612     /*
44613     autoSize : function(){
44614         //this.el.beginMeasure();
44615         this.textEl.setWidth(1);
44616         /*
44617          *  #2804 [new] Tabs in Roojs
44618          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
44619          */
44620         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
44621         //this.el.endMeasure();
44622     //},
44623
44624     /**
44625      * Sets the text for the tab (Note: this also sets the tooltip text)
44626      * @param {String} text The tab's text and tooltip
44627      */
44628     setText : function(text){
44629         this.text = text;
44630         this.textEl.update(text);
44631         this.setTooltip(text);
44632         //if(!this.tabPanel.resizeTabs){
44633         //    this.autoSize();
44634         //}
44635     },
44636     /**
44637      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
44638      */
44639     activate : function(){
44640         this.tabPanel.activate(this.id);
44641     },
44642
44643     /**
44644      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
44645      */
44646     disable : function(){
44647         if(this.tabPanel.active != this){
44648             this.disabled = true;
44649             this.status_node.addClass("disabled");
44650         }
44651     },
44652
44653     /**
44654      * Enables this TabPanelItem if it was previously disabled.
44655      */
44656     enable : function(){
44657         this.disabled = false;
44658         this.status_node.removeClass("disabled");
44659     },
44660
44661     /**
44662      * Sets the content for this TabPanelItem.
44663      * @param {String} content The content
44664      * @param {Boolean} loadScripts true to look for and load scripts
44665      */
44666     setContent : function(content, loadScripts){
44667         this.bodyEl.update(content, loadScripts);
44668     },
44669
44670     /**
44671      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
44672      * @return {Roo.UpdateManager} The UpdateManager
44673      */
44674     getUpdateManager : function(){
44675         return this.bodyEl.getUpdateManager();
44676     },
44677
44678     /**
44679      * Set a URL to be used to load the content for this TabPanelItem.
44680      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
44681      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
44682      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
44683      * @return {Roo.UpdateManager} The UpdateManager
44684      */
44685     setUrl : function(url, params, loadOnce){
44686         if(this.refreshDelegate){
44687             this.un('activate', this.refreshDelegate);
44688         }
44689         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44690         this.on("activate", this.refreshDelegate);
44691         return this.bodyEl.getUpdateManager();
44692     },
44693
44694     /** @private */
44695     _handleRefresh : function(url, params, loadOnce){
44696         if(!loadOnce || !this.loaded){
44697             var updater = this.bodyEl.getUpdateManager();
44698             updater.update(url, params, this._setLoaded.createDelegate(this));
44699         }
44700     },
44701
44702     /**
44703      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
44704      *   Will fail silently if the setUrl method has not been called.
44705      *   This does not activate the panel, just updates its content.
44706      */
44707     refresh : function(){
44708         if(this.refreshDelegate){
44709            this.loaded = false;
44710            this.refreshDelegate();
44711         }
44712     },
44713
44714     /** @private */
44715     _setLoaded : function(){
44716         this.loaded = true;
44717     },
44718
44719     /** @private */
44720     closeClick : function(e){
44721         var o = {};
44722         e.stopEvent();
44723         this.fireEvent("beforeclose", this, o);
44724         if(o.cancel !== true){
44725             this.tabPanel.removeTab(this.id);
44726         }
44727     },
44728     /**
44729      * The text displayed in the tooltip for the close icon.
44730      * @type String
44731      */
44732     closeText : "Close this tab"
44733 });
44734 /**
44735 *    This script refer to:
44736 *    Title: International Telephone Input
44737 *    Author: Jack O'Connor
44738 *    Code version:  v12.1.12
44739 *    Availability: https://github.com/jackocnr/intl-tel-input.git
44740 **/
44741
44742 Roo.bootstrap.form.PhoneInputData = function() {
44743     var d = [
44744       [
44745         "Afghanistan (‫افغانستان‬‎)",
44746         "af",
44747         "93"
44748       ],
44749       [
44750         "Albania (Shqipëri)",
44751         "al",
44752         "355"
44753       ],
44754       [
44755         "Algeria (‫الجزائر‬‎)",
44756         "dz",
44757         "213"
44758       ],
44759       [
44760         "American Samoa",
44761         "as",
44762         "1684"
44763       ],
44764       [
44765         "Andorra",
44766         "ad",
44767         "376"
44768       ],
44769       [
44770         "Angola",
44771         "ao",
44772         "244"
44773       ],
44774       [
44775         "Anguilla",
44776         "ai",
44777         "1264"
44778       ],
44779       [
44780         "Antigua and Barbuda",
44781         "ag",
44782         "1268"
44783       ],
44784       [
44785         "Argentina",
44786         "ar",
44787         "54"
44788       ],
44789       [
44790         "Armenia (Հայաստան)",
44791         "am",
44792         "374"
44793       ],
44794       [
44795         "Aruba",
44796         "aw",
44797         "297"
44798       ],
44799       [
44800         "Australia",
44801         "au",
44802         "61",
44803         0
44804       ],
44805       [
44806         "Austria (Österreich)",
44807         "at",
44808         "43"
44809       ],
44810       [
44811         "Azerbaijan (Azərbaycan)",
44812         "az",
44813         "994"
44814       ],
44815       [
44816         "Bahamas",
44817         "bs",
44818         "1242"
44819       ],
44820       [
44821         "Bahrain (‫البحرين‬‎)",
44822         "bh",
44823         "973"
44824       ],
44825       [
44826         "Bangladesh (বাংলাদেশ)",
44827         "bd",
44828         "880"
44829       ],
44830       [
44831         "Barbados",
44832         "bb",
44833         "1246"
44834       ],
44835       [
44836         "Belarus (Беларусь)",
44837         "by",
44838         "375"
44839       ],
44840       [
44841         "Belgium (België)",
44842         "be",
44843         "32"
44844       ],
44845       [
44846         "Belize",
44847         "bz",
44848         "501"
44849       ],
44850       [
44851         "Benin (Bénin)",
44852         "bj",
44853         "229"
44854       ],
44855       [
44856         "Bermuda",
44857         "bm",
44858         "1441"
44859       ],
44860       [
44861         "Bhutan (འབྲུག)",
44862         "bt",
44863         "975"
44864       ],
44865       [
44866         "Bolivia",
44867         "bo",
44868         "591"
44869       ],
44870       [
44871         "Bosnia and Herzegovina (Босна и Херцеговина)",
44872         "ba",
44873         "387"
44874       ],
44875       [
44876         "Botswana",
44877         "bw",
44878         "267"
44879       ],
44880       [
44881         "Brazil (Brasil)",
44882         "br",
44883         "55"
44884       ],
44885       [
44886         "British Indian Ocean Territory",
44887         "io",
44888         "246"
44889       ],
44890       [
44891         "British Virgin Islands",
44892         "vg",
44893         "1284"
44894       ],
44895       [
44896         "Brunei",
44897         "bn",
44898         "673"
44899       ],
44900       [
44901         "Bulgaria (България)",
44902         "bg",
44903         "359"
44904       ],
44905       [
44906         "Burkina Faso",
44907         "bf",
44908         "226"
44909       ],
44910       [
44911         "Burundi (Uburundi)",
44912         "bi",
44913         "257"
44914       ],
44915       [
44916         "Cambodia (កម្ពុជា)",
44917         "kh",
44918         "855"
44919       ],
44920       [
44921         "Cameroon (Cameroun)",
44922         "cm",
44923         "237"
44924       ],
44925       [
44926         "Canada",
44927         "ca",
44928         "1",
44929         1,
44930         ["204", "226", "236", "249", "250", "289", "306", "343", "365", "387", "403", "416", "418", "431", "437", "438", "450", "506", "514", "519", "548", "579", "581", "587", "604", "613", "639", "647", "672", "705", "709", "742", "778", "780", "782", "807", "819", "825", "867", "873", "902", "905"]
44931       ],
44932       [
44933         "Cape Verde (Kabu Verdi)",
44934         "cv",
44935         "238"
44936       ],
44937       [
44938         "Caribbean Netherlands",
44939         "bq",
44940         "599",
44941         1
44942       ],
44943       [
44944         "Cayman Islands",
44945         "ky",
44946         "1345"
44947       ],
44948       [
44949         "Central African Republic (République centrafricaine)",
44950         "cf",
44951         "236"
44952       ],
44953       [
44954         "Chad (Tchad)",
44955         "td",
44956         "235"
44957       ],
44958       [
44959         "Chile",
44960         "cl",
44961         "56"
44962       ],
44963       [
44964         "China (中国)",
44965         "cn",
44966         "86"
44967       ],
44968       [
44969         "Christmas Island",
44970         "cx",
44971         "61",
44972         2
44973       ],
44974       [
44975         "Cocos (Keeling) Islands",
44976         "cc",
44977         "61",
44978         1
44979       ],
44980       [
44981         "Colombia",
44982         "co",
44983         "57"
44984       ],
44985       [
44986         "Comoros (‫جزر القمر‬‎)",
44987         "km",
44988         "269"
44989       ],
44990       [
44991         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
44992         "cd",
44993         "243"
44994       ],
44995       [
44996         "Congo (Republic) (Congo-Brazzaville)",
44997         "cg",
44998         "242"
44999       ],
45000       [
45001         "Cook Islands",
45002         "ck",
45003         "682"
45004       ],
45005       [
45006         "Costa Rica",
45007         "cr",
45008         "506"
45009       ],
45010       [
45011         "Côte d’Ivoire",
45012         "ci",
45013         "225"
45014       ],
45015       [
45016         "Croatia (Hrvatska)",
45017         "hr",
45018         "385"
45019       ],
45020       [
45021         "Cuba",
45022         "cu",
45023         "53"
45024       ],
45025       [
45026         "Curaçao",
45027         "cw",
45028         "599",
45029         0
45030       ],
45031       [
45032         "Cyprus (Κύπρος)",
45033         "cy",
45034         "357"
45035       ],
45036       [
45037         "Czech Republic (Česká republika)",
45038         "cz",
45039         "420"
45040       ],
45041       [
45042         "Denmark (Danmark)",
45043         "dk",
45044         "45"
45045       ],
45046       [
45047         "Djibouti",
45048         "dj",
45049         "253"
45050       ],
45051       [
45052         "Dominica",
45053         "dm",
45054         "1767"
45055       ],
45056       [
45057         "Dominican Republic (República Dominicana)",
45058         "do",
45059         "1",
45060         2,
45061         ["809", "829", "849"]
45062       ],
45063       [
45064         "Ecuador",
45065         "ec",
45066         "593"
45067       ],
45068       [
45069         "Egypt (‫مصر‬‎)",
45070         "eg",
45071         "20"
45072       ],
45073       [
45074         "El Salvador",
45075         "sv",
45076         "503"
45077       ],
45078       [
45079         "Equatorial Guinea (Guinea Ecuatorial)",
45080         "gq",
45081         "240"
45082       ],
45083       [
45084         "Eritrea",
45085         "er",
45086         "291"
45087       ],
45088       [
45089         "Estonia (Eesti)",
45090         "ee",
45091         "372"
45092       ],
45093       [
45094         "Ethiopia",
45095         "et",
45096         "251"
45097       ],
45098       [
45099         "Falkland Islands (Islas Malvinas)",
45100         "fk",
45101         "500"
45102       ],
45103       [
45104         "Faroe Islands (Føroyar)",
45105         "fo",
45106         "298"
45107       ],
45108       [
45109         "Fiji",
45110         "fj",
45111         "679"
45112       ],
45113       [
45114         "Finland (Suomi)",
45115         "fi",
45116         "358",
45117         0
45118       ],
45119       [
45120         "France",
45121         "fr",
45122         "33"
45123       ],
45124       [
45125         "French Guiana (Guyane française)",
45126         "gf",
45127         "594"
45128       ],
45129       [
45130         "French Polynesia (Polynésie française)",
45131         "pf",
45132         "689"
45133       ],
45134       [
45135         "Gabon",
45136         "ga",
45137         "241"
45138       ],
45139       [
45140         "Gambia",
45141         "gm",
45142         "220"
45143       ],
45144       [
45145         "Georgia (საქართველო)",
45146         "ge",
45147         "995"
45148       ],
45149       [
45150         "Germany (Deutschland)",
45151         "de",
45152         "49"
45153       ],
45154       [
45155         "Ghana (Gaana)",
45156         "gh",
45157         "233"
45158       ],
45159       [
45160         "Gibraltar",
45161         "gi",
45162         "350"
45163       ],
45164       [
45165         "Greece (Ελλάδα)",
45166         "gr",
45167         "30"
45168       ],
45169       [
45170         "Greenland (Kalaallit Nunaat)",
45171         "gl",
45172         "299"
45173       ],
45174       [
45175         "Grenada",
45176         "gd",
45177         "1473"
45178       ],
45179       [
45180         "Guadeloupe",
45181         "gp",
45182         "590",
45183         0
45184       ],
45185       [
45186         "Guam",
45187         "gu",
45188         "1671"
45189       ],
45190       [
45191         "Guatemala",
45192         "gt",
45193         "502"
45194       ],
45195       [
45196         "Guernsey",
45197         "gg",
45198         "44",
45199         1
45200       ],
45201       [
45202         "Guinea (Guinée)",
45203         "gn",
45204         "224"
45205       ],
45206       [
45207         "Guinea-Bissau (Guiné Bissau)",
45208         "gw",
45209         "245"
45210       ],
45211       [
45212         "Guyana",
45213         "gy",
45214         "592"
45215       ],
45216       [
45217         "Haiti",
45218         "ht",
45219         "509"
45220       ],
45221       [
45222         "Honduras",
45223         "hn",
45224         "504"
45225       ],
45226       [
45227         "Hong Kong (香港)",
45228         "hk",
45229         "852"
45230       ],
45231       [
45232         "Hungary (Magyarország)",
45233         "hu",
45234         "36"
45235       ],
45236       [
45237         "Iceland (Ísland)",
45238         "is",
45239         "354"
45240       ],
45241       [
45242         "India (भारत)",
45243         "in",
45244         "91"
45245       ],
45246       [
45247         "Indonesia",
45248         "id",
45249         "62"
45250       ],
45251       [
45252         "Iran (‫ایران‬‎)",
45253         "ir",
45254         "98"
45255       ],
45256       [
45257         "Iraq (‫العراق‬‎)",
45258         "iq",
45259         "964"
45260       ],
45261       [
45262         "Ireland",
45263         "ie",
45264         "353"
45265       ],
45266       [
45267         "Isle of Man",
45268         "im",
45269         "44",
45270         2
45271       ],
45272       [
45273         "Israel (‫ישראל‬‎)",
45274         "il",
45275         "972"
45276       ],
45277       [
45278         "Italy (Italia)",
45279         "it",
45280         "39",
45281         0
45282       ],
45283       [
45284         "Jamaica",
45285         "jm",
45286         "1876"
45287       ],
45288       [
45289         "Japan (日本)",
45290         "jp",
45291         "81"
45292       ],
45293       [
45294         "Jersey",
45295         "je",
45296         "44",
45297         3
45298       ],
45299       [
45300         "Jordan (‫الأردن‬‎)",
45301         "jo",
45302         "962"
45303       ],
45304       [
45305         "Kazakhstan (Казахстан)",
45306         "kz",
45307         "7",
45308         1
45309       ],
45310       [
45311         "Kenya",
45312         "ke",
45313         "254"
45314       ],
45315       [
45316         "Kiribati",
45317         "ki",
45318         "686"
45319       ],
45320       [
45321         "Kosovo",
45322         "xk",
45323         "383"
45324       ],
45325       [
45326         "Kuwait (‫الكويت‬‎)",
45327         "kw",
45328         "965"
45329       ],
45330       [
45331         "Kyrgyzstan (Кыргызстан)",
45332         "kg",
45333         "996"
45334       ],
45335       [
45336         "Laos (ລາວ)",
45337         "la",
45338         "856"
45339       ],
45340       [
45341         "Latvia (Latvija)",
45342         "lv",
45343         "371"
45344       ],
45345       [
45346         "Lebanon (‫لبنان‬‎)",
45347         "lb",
45348         "961"
45349       ],
45350       [
45351         "Lesotho",
45352         "ls",
45353         "266"
45354       ],
45355       [
45356         "Liberia",
45357         "lr",
45358         "231"
45359       ],
45360       [
45361         "Libya (‫ليبيا‬‎)",
45362         "ly",
45363         "218"
45364       ],
45365       [
45366         "Liechtenstein",
45367         "li",
45368         "423"
45369       ],
45370       [
45371         "Lithuania (Lietuva)",
45372         "lt",
45373         "370"
45374       ],
45375       [
45376         "Luxembourg",
45377         "lu",
45378         "352"
45379       ],
45380       [
45381         "Macau (澳門)",
45382         "mo",
45383         "853"
45384       ],
45385       [
45386         "Macedonia (FYROM) (Македонија)",
45387         "mk",
45388         "389"
45389       ],
45390       [
45391         "Madagascar (Madagasikara)",
45392         "mg",
45393         "261"
45394       ],
45395       [
45396         "Malawi",
45397         "mw",
45398         "265"
45399       ],
45400       [
45401         "Malaysia",
45402         "my",
45403         "60"
45404       ],
45405       [
45406         "Maldives",
45407         "mv",
45408         "960"
45409       ],
45410       [
45411         "Mali",
45412         "ml",
45413         "223"
45414       ],
45415       [
45416         "Malta",
45417         "mt",
45418         "356"
45419       ],
45420       [
45421         "Marshall Islands",
45422         "mh",
45423         "692"
45424       ],
45425       [
45426         "Martinique",
45427         "mq",
45428         "596"
45429       ],
45430       [
45431         "Mauritania (‫موريتانيا‬‎)",
45432         "mr",
45433         "222"
45434       ],
45435       [
45436         "Mauritius (Moris)",
45437         "mu",
45438         "230"
45439       ],
45440       [
45441         "Mayotte",
45442         "yt",
45443         "262",
45444         1
45445       ],
45446       [
45447         "Mexico (México)",
45448         "mx",
45449         "52"
45450       ],
45451       [
45452         "Micronesia",
45453         "fm",
45454         "691"
45455       ],
45456       [
45457         "Moldova (Republica Moldova)",
45458         "md",
45459         "373"
45460       ],
45461       [
45462         "Monaco",
45463         "mc",
45464         "377"
45465       ],
45466       [
45467         "Mongolia (Монгол)",
45468         "mn",
45469         "976"
45470       ],
45471       [
45472         "Montenegro (Crna Gora)",
45473         "me",
45474         "382"
45475       ],
45476       [
45477         "Montserrat",
45478         "ms",
45479         "1664"
45480       ],
45481       [
45482         "Morocco (‫المغرب‬‎)",
45483         "ma",
45484         "212",
45485         0
45486       ],
45487       [
45488         "Mozambique (Moçambique)",
45489         "mz",
45490         "258"
45491       ],
45492       [
45493         "Myanmar (Burma) (မြန်မာ)",
45494         "mm",
45495         "95"
45496       ],
45497       [
45498         "Namibia (Namibië)",
45499         "na",
45500         "264"
45501       ],
45502       [
45503         "Nauru",
45504         "nr",
45505         "674"
45506       ],
45507       [
45508         "Nepal (नेपाल)",
45509         "np",
45510         "977"
45511       ],
45512       [
45513         "Netherlands (Nederland)",
45514         "nl",
45515         "31"
45516       ],
45517       [
45518         "New Caledonia (Nouvelle-Calédonie)",
45519         "nc",
45520         "687"
45521       ],
45522       [
45523         "New Zealand",
45524         "nz",
45525         "64"
45526       ],
45527       [
45528         "Nicaragua",
45529         "ni",
45530         "505"
45531       ],
45532       [
45533         "Niger (Nijar)",
45534         "ne",
45535         "227"
45536       ],
45537       [
45538         "Nigeria",
45539         "ng",
45540         "234"
45541       ],
45542       [
45543         "Niue",
45544         "nu",
45545         "683"
45546       ],
45547       [
45548         "Norfolk Island",
45549         "nf",
45550         "672"
45551       ],
45552       [
45553         "North Korea (조선 민주주의 인민 공화국)",
45554         "kp",
45555         "850"
45556       ],
45557       [
45558         "Northern Mariana Islands",
45559         "mp",
45560         "1670"
45561       ],
45562       [
45563         "Norway (Norge)",
45564         "no",
45565         "47",
45566         0
45567       ],
45568       [
45569         "Oman (‫عُمان‬‎)",
45570         "om",
45571         "968"
45572       ],
45573       [
45574         "Pakistan (‫پاکستان‬‎)",
45575         "pk",
45576         "92"
45577       ],
45578       [
45579         "Palau",
45580         "pw",
45581         "680"
45582       ],
45583       [
45584         "Palestine (‫فلسطين‬‎)",
45585         "ps",
45586         "970"
45587       ],
45588       [
45589         "Panama (Panamá)",
45590         "pa",
45591         "507"
45592       ],
45593       [
45594         "Papua New Guinea",
45595         "pg",
45596         "675"
45597       ],
45598       [
45599         "Paraguay",
45600         "py",
45601         "595"
45602       ],
45603       [
45604         "Peru (Perú)",
45605         "pe",
45606         "51"
45607       ],
45608       [
45609         "Philippines",
45610         "ph",
45611         "63"
45612       ],
45613       [
45614         "Poland (Polska)",
45615         "pl",
45616         "48"
45617       ],
45618       [
45619         "Portugal",
45620         "pt",
45621         "351"
45622       ],
45623       [
45624         "Puerto Rico",
45625         "pr",
45626         "1",
45627         3,
45628         ["787", "939"]
45629       ],
45630       [
45631         "Qatar (‫قطر‬‎)",
45632         "qa",
45633         "974"
45634       ],
45635       [
45636         "Réunion (La Réunion)",
45637         "re",
45638         "262",
45639         0
45640       ],
45641       [
45642         "Romania (România)",
45643         "ro",
45644         "40"
45645       ],
45646       [
45647         "Russia (Россия)",
45648         "ru",
45649         "7",
45650         0
45651       ],
45652       [
45653         "Rwanda",
45654         "rw",
45655         "250"
45656       ],
45657       [
45658         "Saint Barthélemy",
45659         "bl",
45660         "590",
45661         1
45662       ],
45663       [
45664         "Saint Helena",
45665         "sh",
45666         "290"
45667       ],
45668       [
45669         "Saint Kitts and Nevis",
45670         "kn",
45671         "1869"
45672       ],
45673       [
45674         "Saint Lucia",
45675         "lc",
45676         "1758"
45677       ],
45678       [
45679         "Saint Martin (Saint-Martin (partie française))",
45680         "mf",
45681         "590",
45682         2
45683       ],
45684       [
45685         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
45686         "pm",
45687         "508"
45688       ],
45689       [
45690         "Saint Vincent and the Grenadines",
45691         "vc",
45692         "1784"
45693       ],
45694       [
45695         "Samoa",
45696         "ws",
45697         "685"
45698       ],
45699       [
45700         "San Marino",
45701         "sm",
45702         "378"
45703       ],
45704       [
45705         "São Tomé and Príncipe (São Tomé e Príncipe)",
45706         "st",
45707         "239"
45708       ],
45709       [
45710         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
45711         "sa",
45712         "966"
45713       ],
45714       [
45715         "Senegal (Sénégal)",
45716         "sn",
45717         "221"
45718       ],
45719       [
45720         "Serbia (Србија)",
45721         "rs",
45722         "381"
45723       ],
45724       [
45725         "Seychelles",
45726         "sc",
45727         "248"
45728       ],
45729       [
45730         "Sierra Leone",
45731         "sl",
45732         "232"
45733       ],
45734       [
45735         "Singapore",
45736         "sg",
45737         "65"
45738       ],
45739       [
45740         "Sint Maarten",
45741         "sx",
45742         "1721"
45743       ],
45744       [
45745         "Slovakia (Slovensko)",
45746         "sk",
45747         "421"
45748       ],
45749       [
45750         "Slovenia (Slovenija)",
45751         "si",
45752         "386"
45753       ],
45754       [
45755         "Solomon Islands",
45756         "sb",
45757         "677"
45758       ],
45759       [
45760         "Somalia (Soomaaliya)",
45761         "so",
45762         "252"
45763       ],
45764       [
45765         "South Africa",
45766         "za",
45767         "27"
45768       ],
45769       [
45770         "South Korea (대한민국)",
45771         "kr",
45772         "82"
45773       ],
45774       [
45775         "South Sudan (‫جنوب السودان‬‎)",
45776         "ss",
45777         "211"
45778       ],
45779       [
45780         "Spain (España)",
45781         "es",
45782         "34"
45783       ],
45784       [
45785         "Sri Lanka (ශ්‍රී ලංකාව)",
45786         "lk",
45787         "94"
45788       ],
45789       [
45790         "Sudan (‫السودان‬‎)",
45791         "sd",
45792         "249"
45793       ],
45794       [
45795         "Suriname",
45796         "sr",
45797         "597"
45798       ],
45799       [
45800         "Svalbard and Jan Mayen",
45801         "sj",
45802         "47",
45803         1
45804       ],
45805       [
45806         "Swaziland",
45807         "sz",
45808         "268"
45809       ],
45810       [
45811         "Sweden (Sverige)",
45812         "se",
45813         "46"
45814       ],
45815       [
45816         "Switzerland (Schweiz)",
45817         "ch",
45818         "41"
45819       ],
45820       [
45821         "Syria (‫سوريا‬‎)",
45822         "sy",
45823         "963"
45824       ],
45825       [
45826         "Taiwan (台灣)",
45827         "tw",
45828         "886"
45829       ],
45830       [
45831         "Tajikistan",
45832         "tj",
45833         "992"
45834       ],
45835       [
45836         "Tanzania",
45837         "tz",
45838         "255"
45839       ],
45840       [
45841         "Thailand (ไทย)",
45842         "th",
45843         "66"
45844       ],
45845       [
45846         "Timor-Leste",
45847         "tl",
45848         "670"
45849       ],
45850       [
45851         "Togo",
45852         "tg",
45853         "228"
45854       ],
45855       [
45856         "Tokelau",
45857         "tk",
45858         "690"
45859       ],
45860       [
45861         "Tonga",
45862         "to",
45863         "676"
45864       ],
45865       [
45866         "Trinidad and Tobago",
45867         "tt",
45868         "1868"
45869       ],
45870       [
45871         "Tunisia (‫تونس‬‎)",
45872         "tn",
45873         "216"
45874       ],
45875       [
45876         "Turkey (Türkiye)",
45877         "tr",
45878         "90"
45879       ],
45880       [
45881         "Turkmenistan",
45882         "tm",
45883         "993"
45884       ],
45885       [
45886         "Turks and Caicos Islands",
45887         "tc",
45888         "1649"
45889       ],
45890       [
45891         "Tuvalu",
45892         "tv",
45893         "688"
45894       ],
45895       [
45896         "U.S. Virgin Islands",
45897         "vi",
45898         "1340"
45899       ],
45900       [
45901         "Uganda",
45902         "ug",
45903         "256"
45904       ],
45905       [
45906         "Ukraine (Україна)",
45907         "ua",
45908         "380"
45909       ],
45910       [
45911         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
45912         "ae",
45913         "971"
45914       ],
45915       [
45916         "United Kingdom",
45917         "gb",
45918         "44",
45919         0
45920       ],
45921       [
45922         "United States",
45923         "us",
45924         "1",
45925         0
45926       ],
45927       [
45928         "Uruguay",
45929         "uy",
45930         "598"
45931       ],
45932       [
45933         "Uzbekistan (Oʻzbekiston)",
45934         "uz",
45935         "998"
45936       ],
45937       [
45938         "Vanuatu",
45939         "vu",
45940         "678"
45941       ],
45942       [
45943         "Vatican City (Città del Vaticano)",
45944         "va",
45945         "39",
45946         1
45947       ],
45948       [
45949         "Venezuela",
45950         "ve",
45951         "58"
45952       ],
45953       [
45954         "Vietnam (Việt Nam)",
45955         "vn",
45956         "84"
45957       ],
45958       [
45959         "Wallis and Futuna (Wallis-et-Futuna)",
45960         "wf",
45961         "681"
45962       ],
45963       [
45964         "Western Sahara (‫الصحراء الغربية‬‎)",
45965         "eh",
45966         "212",
45967         1
45968       ],
45969       [
45970         "Yemen (‫اليمن‬‎)",
45971         "ye",
45972         "967"
45973       ],
45974       [
45975         "Zambia",
45976         "zm",
45977         "260"
45978       ],
45979       [
45980         "Zimbabwe",
45981         "zw",
45982         "263"
45983       ],
45984       [
45985         "Åland Islands",
45986         "ax",
45987         "358",
45988         1
45989       ]
45990   ];
45991   
45992   return d;
45993 }/**
45994 *    This script refer to:
45995 *    Title: International Telephone Input
45996 *    Author: Jack O'Connor
45997 *    Code version:  v12.1.12
45998 *    Availability: https://github.com/jackocnr/intl-tel-input.git
45999 **/
46000
46001 /**
46002  * @class Roo.bootstrap.form.PhoneInput
46003  * @extends Roo.bootstrap.form.TriggerField
46004  * An input with International dial-code selection
46005  
46006  * @cfg {String} defaultDialCode default '+852'
46007  * @cfg {Array} preferedCountries default []
46008   
46009  * @constructor
46010  * Create a new PhoneInput.
46011  * @param {Object} config Configuration options
46012  */
46013
46014 Roo.bootstrap.form.PhoneInput = function(config) {
46015     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
46016 };
46017
46018 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
46019         /**
46020         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
46021         */
46022         listWidth: undefined,
46023         
46024         selectedClass: 'active',
46025         
46026         invalidClass : "has-warning",
46027         
46028         validClass: 'has-success',
46029         
46030         allowed: '0123456789',
46031         
46032         max_length: 15,
46033         
46034         /**
46035          * @cfg {String} defaultDialCode The default dial code when initializing the input
46036          */
46037         defaultDialCode: '+852',
46038         
46039         /**
46040          * @cfg {Array} preferedCountries A list of iso2 in array (e.g. ['hk','us']). Those related countries will show at the top of the input's choices
46041          */
46042         preferedCountries: false,
46043         
46044         getAutoCreate : function()
46045         {
46046             var data = Roo.bootstrap.form.PhoneInputData();
46047             var align = this.labelAlign || this.parentLabelAlign();
46048             var id = Roo.id();
46049             
46050             this.allCountries = [];
46051             this.dialCodeMapping = [];
46052             
46053             for (var i = 0; i < data.length; i++) {
46054               var c = data[i];
46055               this.allCountries[i] = {
46056                 name: c[0],
46057                 iso2: c[1],
46058                 dialCode: c[2],
46059                 priority: c[3] || 0,
46060                 areaCodes: c[4] || null
46061               };
46062               this.dialCodeMapping[c[2]] = {
46063                   name: c[0],
46064                   iso2: c[1],
46065                   priority: c[3] || 0,
46066                   areaCodes: c[4] || null
46067               };
46068             }
46069             
46070             var cfg = {
46071                 cls: 'form-group',
46072                 cn: []
46073             };
46074             
46075             var input =  {
46076                 tag: 'input',
46077                 id : id,
46078                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
46079                 maxlength: this.max_length,
46080                 cls : 'form-control tel-input',
46081                 autocomplete: 'new-password'
46082             };
46083             
46084             var hiddenInput = {
46085                 tag: 'input',
46086                 type: 'hidden',
46087                 cls: 'hidden-tel-input'
46088             };
46089             
46090             if (this.name) {
46091                 hiddenInput.name = this.name;
46092             }
46093             
46094             if (this.disabled) {
46095                 input.disabled = true;
46096             }
46097             
46098             var flag_container = {
46099                 tag: 'div',
46100                 cls: 'flag-box',
46101                 cn: [
46102                     {
46103                         tag: 'div',
46104                         cls: 'flag'
46105                     },
46106                     {
46107                         tag: 'div',
46108                         cls: 'caret'
46109                     }
46110                 ]
46111             };
46112             
46113             var box = {
46114                 tag: 'div',
46115                 cls: this.hasFeedback ? 'has-feedback' : '',
46116                 cn: [
46117                     hiddenInput,
46118                     input,
46119                     {
46120                         tag: 'input',
46121                         cls: 'dial-code-holder',
46122                         disabled: true
46123                     }
46124                 ]
46125             };
46126             
46127             var container = {
46128                 cls: 'roo-select2-container input-group',
46129                 cn: [
46130                     flag_container,
46131                     box
46132                 ]
46133             };
46134             
46135             if (this.fieldLabel.length) {
46136                 var indicator = {
46137                     tag: 'i',
46138                     tooltip: 'This field is required'
46139                 };
46140                 
46141                 var label = {
46142                     tag: 'label',
46143                     'for':  id,
46144                     cls: 'control-label',
46145                     cn: []
46146                 };
46147                 
46148                 var label_text = {
46149                     tag: 'span',
46150                     html: this.fieldLabel
46151                 };
46152                 
46153                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46154                 label.cn = [
46155                     indicator,
46156                     label_text
46157                 ];
46158                 
46159                 if(this.indicatorpos == 'right') {
46160                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46161                     label.cn = [
46162                         label_text,
46163                         indicator
46164                     ];
46165                 }
46166                 
46167                 if(align == 'left') {
46168                     container = {
46169                         tag: 'div',
46170                         cn: [
46171                             container
46172                         ]
46173                     };
46174                     
46175                     if(this.labelWidth > 12){
46176                         label.style = "width: " + this.labelWidth + 'px';
46177                     }
46178                     if(this.labelWidth < 13 && this.labelmd == 0){
46179                         this.labelmd = this.labelWidth;
46180                     }
46181                     if(this.labellg > 0){
46182                         label.cls += ' col-lg-' + this.labellg;
46183                         input.cls += ' col-lg-' + (12 - this.labellg);
46184                     }
46185                     if(this.labelmd > 0){
46186                         label.cls += ' col-md-' + this.labelmd;
46187                         container.cls += ' col-md-' + (12 - this.labelmd);
46188                     }
46189                     if(this.labelsm > 0){
46190                         label.cls += ' col-sm-' + this.labelsm;
46191                         container.cls += ' col-sm-' + (12 - this.labelsm);
46192                     }
46193                     if(this.labelxs > 0){
46194                         label.cls += ' col-xs-' + this.labelxs;
46195                         container.cls += ' col-xs-' + (12 - this.labelxs);
46196                     }
46197                 }
46198             }
46199             
46200             cfg.cn = [
46201                 label,
46202                 container
46203             ];
46204             
46205             var settings = this;
46206             
46207             ['xs','sm','md','lg'].map(function(size){
46208                 if (settings[size]) {
46209                     cfg.cls += ' col-' + size + '-' + settings[size];
46210                 }
46211             });
46212             
46213             this.store = new Roo.data.Store({
46214                 proxy : new Roo.data.MemoryProxy({}),
46215                 reader : new Roo.data.JsonReader({
46216                     fields : [
46217                         {
46218                             'name' : 'name',
46219                             'type' : 'string'
46220                         },
46221                         {
46222                             'name' : 'iso2',
46223                             'type' : 'string'
46224                         },
46225                         {
46226                             'name' : 'dialCode',
46227                             'type' : 'string'
46228                         },
46229                         {
46230                             'name' : 'priority',
46231                             'type' : 'string'
46232                         },
46233                         {
46234                             'name' : 'areaCodes',
46235                             'type' : 'string'
46236                         }
46237                     ]
46238                 })
46239             });
46240             
46241             if(!this.preferedCountries) {
46242                 this.preferedCountries = [
46243                     'hk',
46244                     'gb',
46245                     'us'
46246                 ];
46247             }
46248             
46249             var p = this.preferedCountries.reverse();
46250             
46251             if(p) {
46252                 for (var i = 0; i < p.length; i++) {
46253                     for (var j = 0; j < this.allCountries.length; j++) {
46254                         if(this.allCountries[j].iso2 == p[i]) {
46255                             var t = this.allCountries[j];
46256                             this.allCountries.splice(j,1);
46257                             this.allCountries.unshift(t);
46258                         }
46259                     } 
46260                 }
46261             }
46262             
46263             this.store.proxy.data = {
46264                 success: true,
46265                 data: this.allCountries
46266             };
46267             
46268             return cfg;
46269         },
46270         
46271         initEvents : function()
46272         {
46273             this.createList();
46274             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
46275             
46276             this.indicator = this.indicatorEl();
46277             this.flag = this.flagEl();
46278             this.dialCodeHolder = this.dialCodeHolderEl();
46279             
46280             this.trigger = this.el.select('div.flag-box',true).first();
46281             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
46282             
46283             var _this = this;
46284             
46285             (function(){
46286                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
46287                 _this.list.setWidth(lw);
46288             }).defer(100);
46289             
46290             this.list.on('mouseover', this.onViewOver, this);
46291             this.list.on('mousemove', this.onViewMove, this);
46292             this.inputEl().on("keyup", this.onKeyUp, this);
46293             this.inputEl().on("keypress", this.onKeyPress, this);
46294             
46295             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
46296
46297             this.view = new Roo.View(this.list, this.tpl, {
46298                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
46299             });
46300             
46301             this.view.on('click', this.onViewClick, this);
46302             this.setValue(this.defaultDialCode);
46303         },
46304         
46305         onTriggerClick : function(e)
46306         {
46307             Roo.log('trigger click');
46308             if(this.disabled){
46309                 return;
46310             }
46311             
46312             if(this.isExpanded()){
46313                 this.collapse();
46314                 this.hasFocus = false;
46315             }else {
46316                 this.store.load({});
46317                 this.hasFocus = true;
46318                 this.expand();
46319             }
46320         },
46321         
46322         isExpanded : function()
46323         {
46324             return this.list.isVisible();
46325         },
46326         
46327         collapse : function()
46328         {
46329             if(!this.isExpanded()){
46330                 return;
46331             }
46332             this.list.hide();
46333             Roo.get(document).un('mousedown', this.collapseIf, this);
46334             Roo.get(document).un('mousewheel', this.collapseIf, this);
46335             this.fireEvent('collapse', this);
46336             this.validate();
46337         },
46338         
46339         expand : function()
46340         {
46341             Roo.log('expand');
46342
46343             if(this.isExpanded() || !this.hasFocus){
46344                 return;
46345             }
46346             
46347             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
46348             this.list.setWidth(lw);
46349             
46350             this.list.show();
46351             this.restrictHeight();
46352             
46353             Roo.get(document).on('mousedown', this.collapseIf, this);
46354             Roo.get(document).on('mousewheel', this.collapseIf, this);
46355             
46356             this.fireEvent('expand', this);
46357         },
46358         
46359         restrictHeight : function()
46360         {
46361             this.list.alignTo(this.inputEl(), this.listAlign);
46362             this.list.alignTo(this.inputEl(), this.listAlign);
46363         },
46364         
46365         onViewOver : function(e, t)
46366         {
46367             if(this.inKeyMode){
46368                 return;
46369             }
46370             var item = this.view.findItemFromChild(t);
46371             
46372             if(item){
46373                 var index = this.view.indexOf(item);
46374                 this.select(index, false);
46375             }
46376         },
46377
46378         // private
46379         onViewClick : function(view, doFocus, el, e)
46380         {
46381             var index = this.view.getSelectedIndexes()[0];
46382             
46383             var r = this.store.getAt(index);
46384             
46385             if(r){
46386                 this.onSelect(r, index);
46387             }
46388             if(doFocus !== false && !this.blockFocus){
46389                 this.inputEl().focus();
46390             }
46391         },
46392         
46393         onViewMove : function(e, t)
46394         {
46395             this.inKeyMode = false;
46396         },
46397         
46398         select : function(index, scrollIntoView)
46399         {
46400             this.selectedIndex = index;
46401             this.view.select(index);
46402             if(scrollIntoView !== false){
46403                 var el = this.view.getNode(index);
46404                 if(el){
46405                     this.list.scrollChildIntoView(el, false);
46406                 }
46407             }
46408         },
46409         
46410         createList : function()
46411         {
46412             this.list = Roo.get(document.body).createChild({
46413                 tag: 'ul',
46414                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
46415                 style: 'display:none'
46416             });
46417             
46418             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
46419         },
46420         
46421         collapseIf : function(e)
46422         {
46423             var in_combo  = e.within(this.el);
46424             var in_list =  e.within(this.list);
46425             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
46426             
46427             if (in_combo || in_list || is_list) {
46428                 return;
46429             }
46430             this.collapse();
46431         },
46432         
46433         onSelect : function(record, index)
46434         {
46435             if(this.fireEvent('beforeselect', this, record, index) !== false){
46436                 
46437                 this.setFlagClass(record.data.iso2);
46438                 this.setDialCode(record.data.dialCode);
46439                 this.hasFocus = false;
46440                 this.collapse();
46441                 this.fireEvent('select', this, record, index);
46442             }
46443         },
46444         
46445         flagEl : function()
46446         {
46447             var flag = this.el.select('div.flag',true).first();
46448             if(!flag){
46449                 return false;
46450             }
46451             return flag;
46452         },
46453         
46454         dialCodeHolderEl : function()
46455         {
46456             var d = this.el.select('input.dial-code-holder',true).first();
46457             if(!d){
46458                 return false;
46459             }
46460             return d;
46461         },
46462         
46463         setDialCode : function(v)
46464         {
46465             this.dialCodeHolder.dom.value = '+'+v;
46466         },
46467         
46468         setFlagClass : function(n)
46469         {
46470             this.flag.dom.className = 'flag '+n;
46471         },
46472         
46473         getValue : function()
46474         {
46475             var v = this.inputEl().getValue();
46476             if(this.dialCodeHolder) {
46477                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
46478             }
46479             return v;
46480         },
46481         
46482         setValue : function(v)
46483         {
46484             var d = this.getDialCode(v);
46485             
46486             //invalid dial code
46487             if(v.length == 0 || !d || d.length == 0) {
46488                 if(this.rendered){
46489                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
46490                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
46491                 }
46492                 return;
46493             }
46494             
46495             //valid dial code
46496             this.setFlagClass(this.dialCodeMapping[d].iso2);
46497             this.setDialCode(d);
46498             this.inputEl().dom.value = v.replace('+'+d,'');
46499             this.hiddenEl().dom.value = this.getValue();
46500             
46501             this.validate();
46502         },
46503         
46504         getDialCode : function(v)
46505         {
46506             v = v ||  '';
46507             
46508             if (v.length == 0) {
46509                 return this.dialCodeHolder.dom.value;
46510             }
46511             
46512             var dialCode = "";
46513             if (v.charAt(0) != "+") {
46514                 return false;
46515             }
46516             var numericChars = "";
46517             for (var i = 1; i < v.length; i++) {
46518               var c = v.charAt(i);
46519               if (!isNaN(c)) {
46520                 numericChars += c;
46521                 if (this.dialCodeMapping[numericChars]) {
46522                   dialCode = v.substr(1, i);
46523                 }
46524                 if (numericChars.length == 4) {
46525                   break;
46526                 }
46527               }
46528             }
46529             return dialCode;
46530         },
46531         
46532         reset : function()
46533         {
46534             this.setValue(this.defaultDialCode);
46535             this.validate();
46536         },
46537         
46538         hiddenEl : function()
46539         {
46540             return this.el.select('input.hidden-tel-input',true).first();
46541         },
46542         
46543         // after setting val
46544         onKeyUp : function(e){
46545             this.setValue(this.getValue());
46546         },
46547         
46548         onKeyPress : function(e){
46549             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
46550                 e.stopEvent();
46551             }
46552         }
46553         
46554 });
46555 /**
46556  * @class Roo.bootstrap.form.MoneyField
46557  * @extends Roo.bootstrap.form.ComboBox
46558  * Bootstrap MoneyField class
46559  * 
46560  * @constructor
46561  * Create a new MoneyField.
46562  * @param {Object} config Configuration options
46563  */
46564
46565 Roo.bootstrap.form.MoneyField = function(config) {
46566     
46567     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
46568     
46569 };
46570
46571 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
46572     
46573     /**
46574      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
46575      */
46576     allowDecimals : true,
46577     /**
46578      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
46579      */
46580     decimalSeparator : ".",
46581     /**
46582      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
46583      */
46584     decimalPrecision : 0,
46585     /**
46586      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
46587      */
46588     allowNegative : true,
46589     /**
46590      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
46591      */
46592     allowZero: true,
46593     /**
46594      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
46595      */
46596     minValue : Number.NEGATIVE_INFINITY,
46597     /**
46598      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
46599      */
46600     maxValue : Number.MAX_VALUE,
46601     /**
46602      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
46603      */
46604     minText : "The minimum value for this field is {0}",
46605     /**
46606      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
46607      */
46608     maxText : "The maximum value for this field is {0}",
46609     /**
46610      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
46611      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
46612      */
46613     nanText : "{0} is not a valid number",
46614     /**
46615      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
46616      */
46617     castInt : true,
46618     /**
46619      * @cfg {String} defaults currency of the MoneyField
46620      * value should be in lkey
46621      */
46622     defaultCurrency : false,
46623     /**
46624      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
46625      */
46626     thousandsDelimiter : false,
46627     /**
46628      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
46629      */
46630     max_length: false,
46631     
46632     inputlg : 9,
46633     inputmd : 9,
46634     inputsm : 9,
46635     inputxs : 6,
46636      /**
46637      * @cfg {Roo.data.Store} store  Store to lookup currency??
46638      */
46639     store : false,
46640     
46641     getAutoCreate : function()
46642     {
46643         var align = this.labelAlign || this.parentLabelAlign();
46644         
46645         var id = Roo.id();
46646
46647         var cfg = {
46648             cls: 'form-group',
46649             cn: []
46650         };
46651
46652         var input =  {
46653             tag: 'input',
46654             id : id,
46655             cls : 'form-control roo-money-amount-input',
46656             autocomplete: 'new-password'
46657         };
46658         
46659         var hiddenInput = {
46660             tag: 'input',
46661             type: 'hidden',
46662             id: Roo.id(),
46663             cls: 'hidden-number-input'
46664         };
46665         
46666         if(this.max_length) {
46667             input.maxlength = this.max_length; 
46668         }
46669         
46670         if (this.name) {
46671             hiddenInput.name = this.name;
46672         }
46673
46674         if (this.disabled) {
46675             input.disabled = true;
46676         }
46677
46678         var clg = 12 - this.inputlg;
46679         var cmd = 12 - this.inputmd;
46680         var csm = 12 - this.inputsm;
46681         var cxs = 12 - this.inputxs;
46682         
46683         var container = {
46684             tag : 'div',
46685             cls : 'row roo-money-field',
46686             cn : [
46687                 {
46688                     tag : 'div',
46689                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
46690                     cn : [
46691                         {
46692                             tag : 'div',
46693                             cls: 'roo-select2-container input-group',
46694                             cn: [
46695                                 {
46696                                     tag : 'input',
46697                                     cls : 'form-control roo-money-currency-input',
46698                                     autocomplete: 'new-password',
46699                                     readOnly : 1,
46700                                     name : this.currencyName
46701                                 },
46702                                 {
46703                                     tag :'span',
46704                                     cls : 'input-group-addon',
46705                                     cn : [
46706                                         {
46707                                             tag: 'span',
46708                                             cls: 'caret'
46709                                         }
46710                                     ]
46711                                 }
46712                             ]
46713                         }
46714                     ]
46715                 },
46716                 {
46717                     tag : 'div',
46718                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
46719                     cn : [
46720                         {
46721                             tag: 'div',
46722                             cls: this.hasFeedback ? 'has-feedback' : '',
46723                             cn: [
46724                                 input
46725                             ]
46726                         }
46727                     ]
46728                 }
46729             ]
46730             
46731         };
46732         
46733         if (this.fieldLabel.length) {
46734             var indicator = {
46735                 tag: 'i',
46736                 tooltip: 'This field is required'
46737             };
46738
46739             var label = {
46740                 tag: 'label',
46741                 'for':  id,
46742                 cls: 'control-label',
46743                 cn: []
46744             };
46745
46746             var label_text = {
46747                 tag: 'span',
46748                 html: this.fieldLabel
46749             };
46750
46751             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46752             label.cn = [
46753                 indicator,
46754                 label_text
46755             ];
46756
46757             if(this.indicatorpos == 'right') {
46758                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46759                 label.cn = [
46760                     label_text,
46761                     indicator
46762                 ];
46763             }
46764
46765             if(align == 'left') {
46766                 container = {
46767                     tag: 'div',
46768                     cn: [
46769                         container
46770                     ]
46771                 };
46772
46773                 if(this.labelWidth > 12){
46774                     label.style = "width: " + this.labelWidth + 'px';
46775                 }
46776                 if(this.labelWidth < 13 && this.labelmd == 0){
46777                     this.labelmd = this.labelWidth;
46778                 }
46779                 if(this.labellg > 0){
46780                     label.cls += ' col-lg-' + this.labellg;
46781                     input.cls += ' col-lg-' + (12 - this.labellg);
46782                 }
46783                 if(this.labelmd > 0){
46784                     label.cls += ' col-md-' + this.labelmd;
46785                     container.cls += ' col-md-' + (12 - this.labelmd);
46786                 }
46787                 if(this.labelsm > 0){
46788                     label.cls += ' col-sm-' + this.labelsm;
46789                     container.cls += ' col-sm-' + (12 - this.labelsm);
46790                 }
46791                 if(this.labelxs > 0){
46792                     label.cls += ' col-xs-' + this.labelxs;
46793                     container.cls += ' col-xs-' + (12 - this.labelxs);
46794                 }
46795             }
46796         }
46797
46798         cfg.cn = [
46799             label,
46800             container,
46801             hiddenInput
46802         ];
46803         
46804         var settings = this;
46805
46806         ['xs','sm','md','lg'].map(function(size){
46807             if (settings[size]) {
46808                 cfg.cls += ' col-' + size + '-' + settings[size];
46809             }
46810         });
46811         
46812         return cfg;
46813     },
46814     
46815     initEvents : function()
46816     {
46817         this.indicator = this.indicatorEl();
46818         
46819         this.initCurrencyEvent();
46820         
46821         this.initNumberEvent();
46822     },
46823     
46824     initCurrencyEvent : function()
46825     {
46826         if (!this.store) {
46827             throw "can not find store for combo";
46828         }
46829         
46830         this.store = Roo.factory(this.store, Roo.data);
46831         this.store.parent = this;
46832         
46833         this.createList();
46834         
46835         this.triggerEl = this.el.select('.input-group-addon', true).first();
46836         
46837         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
46838         
46839         var _this = this;
46840         
46841         (function(){
46842             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
46843             _this.list.setWidth(lw);
46844         }).defer(100);
46845         
46846         this.list.on('mouseover', this.onViewOver, this);
46847         this.list.on('mousemove', this.onViewMove, this);
46848         this.list.on('scroll', this.onViewScroll, this);
46849         
46850         if(!this.tpl){
46851             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
46852         }
46853         
46854         this.view = new Roo.View(this.list, this.tpl, {
46855             singleSelect:true, store: this.store, selectedClass: this.selectedClass
46856         });
46857         
46858         this.view.on('click', this.onViewClick, this);
46859         
46860         this.store.on('beforeload', this.onBeforeLoad, this);
46861         this.store.on('load', this.onLoad, this);
46862         this.store.on('loadexception', this.onLoadException, this);
46863         
46864         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
46865             "up" : function(e){
46866                 this.inKeyMode = true;
46867                 this.selectPrev();
46868             },
46869
46870             "down" : function(e){
46871                 if(!this.isExpanded()){
46872                     this.onTriggerClick();
46873                 }else{
46874                     this.inKeyMode = true;
46875                     this.selectNext();
46876                 }
46877             },
46878
46879             "enter" : function(e){
46880                 this.collapse();
46881                 
46882                 if(this.fireEvent("specialkey", this, e)){
46883                     this.onViewClick(false);
46884                 }
46885                 
46886                 return true;
46887             },
46888
46889             "esc" : function(e){
46890                 this.collapse();
46891             },
46892
46893             "tab" : function(e){
46894                 this.collapse();
46895                 
46896                 if(this.fireEvent("specialkey", this, e)){
46897                     this.onViewClick(false);
46898                 }
46899                 
46900                 return true;
46901             },
46902
46903             scope : this,
46904
46905             doRelay : function(foo, bar, hname){
46906                 if(hname == 'down' || this.scope.isExpanded()){
46907                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
46908                 }
46909                 return true;
46910             },
46911
46912             forceKeyDown: true
46913         });
46914         
46915         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
46916         
46917     },
46918     
46919     initNumberEvent : function(e)
46920     {
46921         this.inputEl().on("keydown" , this.fireKey,  this);
46922         this.inputEl().on("focus", this.onFocus,  this);
46923         this.inputEl().on("blur", this.onBlur,  this);
46924         
46925         this.inputEl().relayEvent('keyup', this);
46926         
46927         if(this.indicator){
46928             this.indicator.addClass('invisible');
46929         }
46930  
46931         this.originalValue = this.getValue();
46932         
46933         if(this.validationEvent == 'keyup'){
46934             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
46935             this.inputEl().on('keyup', this.filterValidation, this);
46936         }
46937         else if(this.validationEvent !== false){
46938             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
46939         }
46940         
46941         if(this.selectOnFocus){
46942             this.on("focus", this.preFocus, this);
46943             
46944         }
46945         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
46946             this.inputEl().on("keypress", this.filterKeys, this);
46947         } else {
46948             this.inputEl().relayEvent('keypress', this);
46949         }
46950         
46951         var allowed = "0123456789";
46952         
46953         if(this.allowDecimals){
46954             allowed += this.decimalSeparator;
46955         }
46956         
46957         if(this.allowNegative){
46958             allowed += "-";
46959         }
46960         
46961         if(this.thousandsDelimiter) {
46962             allowed += ",";
46963         }
46964         
46965         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
46966         
46967         var keyPress = function(e){
46968             
46969             var k = e.getKey();
46970             
46971             var c = e.getCharCode();
46972             
46973             if(
46974                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
46975                     allowed.indexOf(String.fromCharCode(c)) === -1
46976             ){
46977                 e.stopEvent();
46978                 return;
46979             }
46980             
46981             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
46982                 return;
46983             }
46984             
46985             if(allowed.indexOf(String.fromCharCode(c)) === -1){
46986                 e.stopEvent();
46987             }
46988         };
46989         
46990         this.inputEl().on("keypress", keyPress, this);
46991         
46992     },
46993     
46994     onTriggerClick : function(e)
46995     {   
46996         if(this.disabled){
46997             return;
46998         }
46999         
47000         this.page = 0;
47001         this.loadNext = false;
47002         
47003         if(this.isExpanded()){
47004             this.collapse();
47005             return;
47006         }
47007         
47008         this.hasFocus = true;
47009         
47010         if(this.triggerAction == 'all') {
47011             this.doQuery(this.allQuery, true);
47012             return;
47013         }
47014         
47015         this.doQuery(this.getRawValue());
47016     },
47017     
47018     getCurrency : function()
47019     {   
47020         var v = this.currencyEl().getValue();
47021         
47022         return v;
47023     },
47024     
47025     restrictHeight : function()
47026     {
47027         this.list.alignTo(this.currencyEl(), this.listAlign);
47028         this.list.alignTo(this.currencyEl(), this.listAlign);
47029     },
47030     
47031     onViewClick : function(view, doFocus, el, e)
47032     {
47033         var index = this.view.getSelectedIndexes()[0];
47034         
47035         var r = this.store.getAt(index);
47036         
47037         if(r){
47038             this.onSelect(r, index);
47039         }
47040     },
47041     
47042     onSelect : function(record, index){
47043         
47044         if(this.fireEvent('beforeselect', this, record, index) !== false){
47045         
47046             this.setFromCurrencyData(index > -1 ? record.data : false);
47047             
47048             this.collapse();
47049             
47050             this.fireEvent('select', this, record, index);
47051         }
47052     },
47053     
47054     setFromCurrencyData : function(o)
47055     {
47056         var currency = '';
47057         
47058         this.lastCurrency = o;
47059         
47060         if (this.currencyField) {
47061             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
47062         } else {
47063             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
47064         }
47065         
47066         this.lastSelectionText = currency;
47067         
47068         //setting default currency
47069         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
47070             this.setCurrency(this.defaultCurrency);
47071             return;
47072         }
47073         
47074         this.setCurrency(currency);
47075     },
47076     
47077     setFromData : function(o)
47078     {
47079         var c = {};
47080         
47081         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
47082         
47083         this.setFromCurrencyData(c);
47084         
47085         var value = '';
47086         
47087         if (this.name) {
47088             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
47089         } else {
47090             Roo.log('no value set for '+ (this.name ? this.name : this.id));
47091         }
47092         
47093         this.setValue(value);
47094         
47095     },
47096     
47097     setCurrency : function(v)
47098     {   
47099         this.currencyValue = v;
47100         
47101         if(this.rendered){
47102             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
47103             this.validate();
47104         }
47105     },
47106     
47107     setValue : function(v)
47108     {
47109         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
47110         
47111         this.value = v;
47112         
47113         if(this.rendered){
47114             
47115             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
47116             
47117             this.inputEl().dom.value = (v == '') ? '' :
47118                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
47119             
47120             if(!this.allowZero && v === '0') {
47121                 this.hiddenEl().dom.value = '';
47122                 this.inputEl().dom.value = '';
47123             }
47124             
47125             this.validate();
47126         }
47127     },
47128     
47129     getRawValue : function()
47130     {
47131         var v = this.inputEl().getValue();
47132         
47133         return v;
47134     },
47135     
47136     getValue : function()
47137     {
47138         return this.fixPrecision(this.parseValue(this.getRawValue()));
47139     },
47140     
47141     parseValue : function(value)
47142     {
47143         if(this.thousandsDelimiter) {
47144             value += "";
47145             r = new RegExp(",", "g");
47146             value = value.replace(r, "");
47147         }
47148         
47149         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
47150         return isNaN(value) ? '' : value;
47151         
47152     },
47153     
47154     fixPrecision : function(value)
47155     {
47156         if(this.thousandsDelimiter) {
47157             value += "";
47158             r = new RegExp(",", "g");
47159             value = value.replace(r, "");
47160         }
47161         
47162         var nan = isNaN(value);
47163         
47164         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
47165             return nan ? '' : value;
47166         }
47167         return parseFloat(value).toFixed(this.decimalPrecision);
47168     },
47169     
47170     decimalPrecisionFcn : function(v)
47171     {
47172         return Math.floor(v);
47173     },
47174     
47175     validateValue : function(value)
47176     {
47177         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
47178             return false;
47179         }
47180         
47181         var num = this.parseValue(value);
47182         
47183         if(isNaN(num)){
47184             this.markInvalid(String.format(this.nanText, value));
47185             return false;
47186         }
47187         
47188         if(num < this.minValue){
47189             this.markInvalid(String.format(this.minText, this.minValue));
47190             return false;
47191         }
47192         
47193         if(num > this.maxValue){
47194             this.markInvalid(String.format(this.maxText, this.maxValue));
47195             return false;
47196         }
47197         
47198         return true;
47199     },
47200     
47201     validate : function()
47202     {
47203         if(this.disabled || this.allowBlank){
47204             this.markValid();
47205             return true;
47206         }
47207         
47208         var currency = this.getCurrency();
47209         
47210         if(this.validateValue(this.getRawValue()) && currency.length){
47211             this.markValid();
47212             return true;
47213         }
47214         
47215         this.markInvalid();
47216         return false;
47217     },
47218     
47219     getName: function()
47220     {
47221         return this.name;
47222     },
47223     
47224     beforeBlur : function()
47225     {
47226         if(!this.castInt){
47227             return;
47228         }
47229         
47230         var v = this.parseValue(this.getRawValue());
47231         
47232         if(v || v == 0){
47233             this.setValue(v);
47234         }
47235     },
47236     
47237     onBlur : function()
47238     {
47239         this.beforeBlur();
47240         
47241         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
47242             //this.el.removeClass(this.focusClass);
47243         }
47244         
47245         this.hasFocus = false;
47246         
47247         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
47248             this.validate();
47249         }
47250         
47251         var v = this.getValue();
47252         
47253         if(String(v) !== String(this.startValue)){
47254             this.fireEvent('change', this, v, this.startValue);
47255         }
47256         
47257         this.fireEvent("blur", this);
47258     },
47259     
47260     inputEl : function()
47261     {
47262         return this.el.select('.roo-money-amount-input', true).first();
47263     },
47264     
47265     currencyEl : function()
47266     {
47267         return this.el.select('.roo-money-currency-input', true).first();
47268     },
47269     
47270     hiddenEl : function()
47271     {
47272         return this.el.select('input.hidden-number-input',true).first();
47273     }
47274     
47275 });/**
47276  * @class Roo.bootstrap.BezierSignature
47277  * @extends Roo.bootstrap.Component
47278  * Bootstrap BezierSignature class
47279  * This script refer to:
47280  *    Title: Signature Pad
47281  *    Author: szimek
47282  *    Availability: https://github.com/szimek/signature_pad
47283  *
47284  * @constructor
47285  * Create a new BezierSignature
47286  * @param {Object} config The config object
47287  */
47288
47289 Roo.bootstrap.BezierSignature = function(config){
47290     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
47291     this.addEvents({
47292         "resize" : true
47293     });
47294 };
47295
47296 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
47297 {
47298      
47299     curve_data: [],
47300     
47301     is_empty: true,
47302     
47303     mouse_btn_down: true,
47304     
47305     /**
47306      * @cfg {int} canvas height
47307      */
47308     canvas_height: '200px',
47309     
47310     /**
47311      * @cfg {float|function} Radius of a single dot.
47312      */ 
47313     dot_size: false,
47314     
47315     /**
47316      * @cfg {float} Minimum width of a line. Defaults to 0.5.
47317      */
47318     min_width: 0.5,
47319     
47320     /**
47321      * @cfg {float} Maximum width of a line. Defaults to 2.5.
47322      */
47323     max_width: 2.5,
47324     
47325     /**
47326      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
47327      */
47328     throttle: 16,
47329     
47330     /**
47331      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
47332      */
47333     min_distance: 5,
47334     
47335     /**
47336      * @cfg {string} Color used to clear the background. Can be any color format accepted by context.fillStyle. Defaults to "rgba(0,0,0,0)" (transparent black). Use a non-transparent color e.g. "rgb(255,255,255)" (opaque white) if you'd like to save signatures as JPEG images.
47337      */
47338     bg_color: 'rgba(0, 0, 0, 0)',
47339     
47340     /**
47341      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
47342      */
47343     dot_color: 'black',
47344     
47345     /**
47346      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
47347      */ 
47348     velocity_filter_weight: 0.7,
47349     
47350     /**
47351      * @cfg {function} Callback when stroke begin. 
47352      */
47353     onBegin: false,
47354     
47355     /**
47356      * @cfg {function} Callback when stroke end.
47357      */
47358     onEnd: false,
47359     
47360     getAutoCreate : function()
47361     {
47362         var cls = 'roo-signature column';
47363         
47364         if(this.cls){
47365             cls += ' ' + this.cls;
47366         }
47367         
47368         var col_sizes = [
47369             'lg',
47370             'md',
47371             'sm',
47372             'xs'
47373         ];
47374         
47375         for(var i = 0; i < col_sizes.length; i++) {
47376             if(this[col_sizes[i]]) {
47377                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
47378             }
47379         }
47380         
47381         var cfg = {
47382             tag: 'div',
47383             cls: cls,
47384             cn: [
47385                 {
47386                     tag: 'div',
47387                     cls: 'roo-signature-body',
47388                     cn: [
47389                         {
47390                             tag: 'canvas',
47391                             cls: 'roo-signature-body-canvas',
47392                             height: this.canvas_height,
47393                             width: this.canvas_width
47394                         }
47395                     ]
47396                 },
47397                 {
47398                     tag: 'input',
47399                     type: 'file',
47400                     style: 'display: none'
47401                 }
47402             ]
47403         };
47404         
47405         return cfg;
47406     },
47407     
47408     initEvents: function() 
47409     {
47410         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
47411         
47412         var canvas = this.canvasEl();
47413         
47414         // mouse && touch event swapping...
47415         canvas.dom.style.touchAction = 'none';
47416         canvas.dom.style.msTouchAction = 'none';
47417         
47418         this.mouse_btn_down = false;
47419         canvas.on('mousedown', this._handleMouseDown, this);
47420         canvas.on('mousemove', this._handleMouseMove, this);
47421         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
47422         
47423         if (window.PointerEvent) {
47424             canvas.on('pointerdown', this._handleMouseDown, this);
47425             canvas.on('pointermove', this._handleMouseMove, this);
47426             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
47427         }
47428         
47429         if ('ontouchstart' in window) {
47430             canvas.on('touchstart', this._handleTouchStart, this);
47431             canvas.on('touchmove', this._handleTouchMove, this);
47432             canvas.on('touchend', this._handleTouchEnd, this);
47433         }
47434         
47435         Roo.EventManager.onWindowResize(this.resize, this, true);
47436         
47437         // file input event
47438         this.fileEl().on('change', this.uploadImage, this);
47439         
47440         this.clear();
47441         
47442         this.resize();
47443     },
47444     
47445     resize: function(){
47446         
47447         var canvas = this.canvasEl().dom;
47448         var ctx = this.canvasElCtx();
47449         var img_data = false;
47450         
47451         if(canvas.width > 0) {
47452             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
47453         }
47454         // setting canvas width will clean img data
47455         canvas.width = 0;
47456         
47457         var style = window.getComputedStyle ? 
47458             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
47459             
47460         var padding_left = parseInt(style.paddingLeft) || 0;
47461         var padding_right = parseInt(style.paddingRight) || 0;
47462         
47463         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
47464         
47465         if(img_data) {
47466             ctx.putImageData(img_data, 0, 0);
47467         }
47468     },
47469     
47470     _handleMouseDown: function(e)
47471     {
47472         if (e.browserEvent.which === 1) {
47473             this.mouse_btn_down = true;
47474             this.strokeBegin(e);
47475         }
47476     },
47477     
47478     _handleMouseMove: function (e)
47479     {
47480         if (this.mouse_btn_down) {
47481             this.strokeMoveUpdate(e);
47482         }
47483     },
47484     
47485     _handleMouseUp: function (e)
47486     {
47487         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
47488             this.mouse_btn_down = false;
47489             this.strokeEnd(e);
47490         }
47491     },
47492     
47493     _handleTouchStart: function (e) {
47494         
47495         e.preventDefault();
47496         if (e.browserEvent.targetTouches.length === 1) {
47497             // var touch = e.browserEvent.changedTouches[0];
47498             // this.strokeBegin(touch);
47499             
47500              this.strokeBegin(e); // assume e catching the correct xy...
47501         }
47502     },
47503     
47504     _handleTouchMove: function (e) {
47505         e.preventDefault();
47506         // var touch = event.targetTouches[0];
47507         // _this._strokeMoveUpdate(touch);
47508         this.strokeMoveUpdate(e);
47509     },
47510     
47511     _handleTouchEnd: function (e) {
47512         var wasCanvasTouched = e.target === this.canvasEl().dom;
47513         if (wasCanvasTouched) {
47514             e.preventDefault();
47515             // var touch = event.changedTouches[0];
47516             // _this._strokeEnd(touch);
47517             this.strokeEnd(e);
47518         }
47519     },
47520     
47521     reset: function () {
47522         this._lastPoints = [];
47523         this._lastVelocity = 0;
47524         this._lastWidth = (this.min_width + this.max_width) / 2;
47525         this.canvasElCtx().fillStyle = this.dot_color;
47526     },
47527     
47528     strokeMoveUpdate: function(e)
47529     {
47530         this.strokeUpdate(e);
47531         
47532         if (this.throttle) {
47533             this.throttleStroke(this.strokeUpdate, this.throttle);
47534         }
47535         else {
47536             this.strokeUpdate(e);
47537         }
47538     },
47539     
47540     strokeBegin: function(e)
47541     {
47542         var newPointGroup = {
47543             color: this.dot_color,
47544             points: []
47545         };
47546         
47547         if (typeof this.onBegin === 'function') {
47548             this.onBegin(e);
47549         }
47550         
47551         this.curve_data.push(newPointGroup);
47552         this.reset();
47553         this.strokeUpdate(e);
47554     },
47555     
47556     strokeUpdate: function(e)
47557     {
47558         var rect = this.canvasEl().dom.getBoundingClientRect();
47559         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
47560         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
47561         var lastPoints = lastPointGroup.points;
47562         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
47563         var isLastPointTooClose = lastPoint
47564             ? point.distanceTo(lastPoint) <= this.min_distance
47565             : false;
47566         var color = lastPointGroup.color;
47567         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
47568             var curve = this.addPoint(point);
47569             if (!lastPoint) {
47570                 this.drawDot({color: color, point: point});
47571             }
47572             else if (curve) {
47573                 this.drawCurve({color: color, curve: curve});
47574             }
47575             lastPoints.push({
47576                 time: point.time,
47577                 x: point.x,
47578                 y: point.y
47579             });
47580         }
47581     },
47582     
47583     strokeEnd: function(e)
47584     {
47585         this.strokeUpdate(e);
47586         if (typeof this.onEnd === 'function') {
47587             this.onEnd(e);
47588         }
47589     },
47590     
47591     addPoint:  function (point) {
47592         var _lastPoints = this._lastPoints;
47593         _lastPoints.push(point);
47594         if (_lastPoints.length > 2) {
47595             if (_lastPoints.length === 3) {
47596                 _lastPoints.unshift(_lastPoints[0]);
47597             }
47598             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
47599             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
47600             _lastPoints.shift();
47601             return curve;
47602         }
47603         return null;
47604     },
47605     
47606     calculateCurveWidths: function (startPoint, endPoint) {
47607         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
47608             (1 - this.velocity_filter_weight) * this._lastVelocity;
47609
47610         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
47611         var widths = {
47612             end: newWidth,
47613             start: this._lastWidth
47614         };
47615         
47616         this._lastVelocity = velocity;
47617         this._lastWidth = newWidth;
47618         return widths;
47619     },
47620     
47621     drawDot: function (_a) {
47622         var color = _a.color, point = _a.point;
47623         var ctx = this.canvasElCtx();
47624         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
47625         ctx.beginPath();
47626         this.drawCurveSegment(point.x, point.y, width);
47627         ctx.closePath();
47628         ctx.fillStyle = color;
47629         ctx.fill();
47630     },
47631     
47632     drawCurve: function (_a) {
47633         var color = _a.color, curve = _a.curve;
47634         var ctx = this.canvasElCtx();
47635         var widthDelta = curve.endWidth - curve.startWidth;
47636         var drawSteps = Math.floor(curve.length()) * 2;
47637         ctx.beginPath();
47638         ctx.fillStyle = color;
47639         for (var i = 0; i < drawSteps; i += 1) {
47640         var t = i / drawSteps;
47641         var tt = t * t;
47642         var ttt = tt * t;
47643         var u = 1 - t;
47644         var uu = u * u;
47645         var uuu = uu * u;
47646         var x = uuu * curve.startPoint.x;
47647         x += 3 * uu * t * curve.control1.x;
47648         x += 3 * u * tt * curve.control2.x;
47649         x += ttt * curve.endPoint.x;
47650         var y = uuu * curve.startPoint.y;
47651         y += 3 * uu * t * curve.control1.y;
47652         y += 3 * u * tt * curve.control2.y;
47653         y += ttt * curve.endPoint.y;
47654         var width = curve.startWidth + ttt * widthDelta;
47655         this.drawCurveSegment(x, y, width);
47656         }
47657         ctx.closePath();
47658         ctx.fill();
47659     },
47660     
47661     drawCurveSegment: function (x, y, width) {
47662         var ctx = this.canvasElCtx();
47663         ctx.moveTo(x, y);
47664         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
47665         this.is_empty = false;
47666     },
47667     
47668     clear: function()
47669     {
47670         var ctx = this.canvasElCtx();
47671         var canvas = this.canvasEl().dom;
47672         ctx.fillStyle = this.bg_color;
47673         ctx.clearRect(0, 0, canvas.width, canvas.height);
47674         ctx.fillRect(0, 0, canvas.width, canvas.height);
47675         this.curve_data = [];
47676         this.reset();
47677         this.is_empty = true;
47678     },
47679     
47680     fileEl: function()
47681     {
47682         return  this.el.select('input',true).first();
47683     },
47684     
47685     canvasEl: function()
47686     {
47687         return this.el.select('canvas',true).first();
47688     },
47689     
47690     canvasElCtx: function()
47691     {
47692         return this.el.select('canvas',true).first().dom.getContext('2d');
47693     },
47694     
47695     getImage: function(type)
47696     {
47697         if(this.is_empty) {
47698             return false;
47699         }
47700         
47701         // encryption ?
47702         return this.canvasEl().dom.toDataURL('image/'+type, 1);
47703     },
47704     
47705     drawFromImage: function(img_src)
47706     {
47707         var img = new Image();
47708         
47709         img.onload = function(){
47710             this.canvasElCtx().drawImage(img, 0, 0);
47711         }.bind(this);
47712         
47713         img.src = img_src;
47714         
47715         this.is_empty = false;
47716     },
47717     
47718     selectImage: function()
47719     {
47720         this.fileEl().dom.click();
47721     },
47722     
47723     uploadImage: function(e)
47724     {
47725         var reader = new FileReader();
47726         
47727         reader.onload = function(e){
47728             var img = new Image();
47729             img.onload = function(){
47730                 this.reset();
47731                 this.canvasElCtx().drawImage(img, 0, 0);
47732             }.bind(this);
47733             img.src = e.target.result;
47734         }.bind(this);
47735         
47736         reader.readAsDataURL(e.target.files[0]);
47737     },
47738     
47739     // Bezier Point Constructor
47740     Point: (function () {
47741         function Point(x, y, time) {
47742             this.x = x;
47743             this.y = y;
47744             this.time = time || Date.now();
47745         }
47746         Point.prototype.distanceTo = function (start) {
47747             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
47748         };
47749         Point.prototype.equals = function (other) {
47750             return this.x === other.x && this.y === other.y && this.time === other.time;
47751         };
47752         Point.prototype.velocityFrom = function (start) {
47753             return this.time !== start.time
47754             ? this.distanceTo(start) / (this.time - start.time)
47755             : 0;
47756         };
47757         return Point;
47758     }()),
47759     
47760     
47761     // Bezier Constructor
47762     Bezier: (function () {
47763         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
47764             this.startPoint = startPoint;
47765             this.control2 = control2;
47766             this.control1 = control1;
47767             this.endPoint = endPoint;
47768             this.startWidth = startWidth;
47769             this.endWidth = endWidth;
47770         }
47771         Bezier.fromPoints = function (points, widths, scope) {
47772             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
47773             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
47774             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
47775         };
47776         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
47777             var dx1 = s1.x - s2.x;
47778             var dy1 = s1.y - s2.y;
47779             var dx2 = s2.x - s3.x;
47780             var dy2 = s2.y - s3.y;
47781             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
47782             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
47783             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
47784             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
47785             var dxm = m1.x - m2.x;
47786             var dym = m1.y - m2.y;
47787             var k = l2 / (l1 + l2);
47788             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
47789             var tx = s2.x - cm.x;
47790             var ty = s2.y - cm.y;
47791             return {
47792                 c1: new scope.Point(m1.x + tx, m1.y + ty),
47793                 c2: new scope.Point(m2.x + tx, m2.y + ty)
47794             };
47795         };
47796         Bezier.prototype.length = function () {
47797             var steps = 10;
47798             var length = 0;
47799             var px;
47800             var py;
47801             for (var i = 0; i <= steps; i += 1) {
47802                 var t = i / steps;
47803                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
47804                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
47805                 if (i > 0) {
47806                     var xdiff = cx - px;
47807                     var ydiff = cy - py;
47808                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
47809                 }
47810                 px = cx;
47811                 py = cy;
47812             }
47813             return length;
47814         };
47815         Bezier.prototype.point = function (t, start, c1, c2, end) {
47816             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
47817             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
47818             + (3.0 * c2 * (1.0 - t) * t * t)
47819             + (end * t * t * t);
47820         };
47821         return Bezier;
47822     }()),
47823     
47824     throttleStroke: function(fn, wait) {
47825       if (wait === void 0) { wait = 250; }
47826       var previous = 0;
47827       var timeout = null;
47828       var result;
47829       var storedContext;
47830       var storedArgs;
47831       var later = function () {
47832           previous = Date.now();
47833           timeout = null;
47834           result = fn.apply(storedContext, storedArgs);
47835           if (!timeout) {
47836               storedContext = null;
47837               storedArgs = [];
47838           }
47839       };
47840       return function wrapper() {
47841           var args = [];
47842           for (var _i = 0; _i < arguments.length; _i++) {
47843               args[_i] = arguments[_i];
47844           }
47845           var now = Date.now();
47846           var remaining = wait - (now - previous);
47847           storedContext = this;
47848           storedArgs = args;
47849           if (remaining <= 0 || remaining > wait) {
47850               if (timeout) {
47851                   clearTimeout(timeout);
47852                   timeout = null;
47853               }
47854               previous = now;
47855               result = fn.apply(storedContext, storedArgs);
47856               if (!timeout) {
47857                   storedContext = null;
47858                   storedArgs = [];
47859               }
47860           }
47861           else if (!timeout) {
47862               timeout = window.setTimeout(later, remaining);
47863           }
47864           return result;
47865       };
47866   }
47867   
47868 });
47869
47870  
47871
47872  // old names for form elements
47873 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
47874 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
47875 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
47876 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
47877 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
47878 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
47879 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
47880 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
47881 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
47882 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
47883 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
47884 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
47885 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
47886 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
47887 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
47888 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
47889 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
47890 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
47891 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
47892 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
47893 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
47894 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
47895 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
47896 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
47897 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
47898 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
47899
47900 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
47901 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
47902
47903 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
47904 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
47905
47906 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
47907 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
47908 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
47909 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
47910